package kha; import haxe.io.Bytes; import haxe.io.BytesData; import kha.kore.graphics4.TextureUnit; import kha.graphics4.TextureFormat; import kha.graphics4.DepthStencilFormat; import kha.graphics4.Usage; @:headerCode(" #include #include #include #include #include enum KhaImageType { KhaImageTypeNone, KhaImageTypeTexture, KhaImageTypeRenderTarget, KhaImageTypeTextureArray }; ") @:headerClassCode(" KhaImageType imageType; int originalWidth; int originalHeight; uint8_t *imageData; bool ownsImageData; kinc_g4_texture_t texture; kinc_g4_render_target_t renderTarget; kinc_g4_texture_array_t textureArray; ") class Image implements Canvas implements Resource { var myFormat: TextureFormat; var readable: Bool; var graphics1: kha.graphics1.Graphics; var graphics2: kha.graphics2.Graphics; var graphics4: kha.graphics4.Graphics; public static function fromVideo(video: Video): Image { var image = new Image(false, false); image.myFormat = RGBA32; image.initVideo(cast(video, kha.kore.Video)); return image; } public static function create(width: Int, height: Int, format: TextureFormat = null, usage: Usage = null, readable: Bool = false): Image { return _create2(width, height, format == null ? TextureFormat.RGBA32 : format, readable, false, NoDepthAndStencil, 0); } public static function create3D(width: Int, height: Int, depth: Int, format: TextureFormat = null, usage: Usage = null, readable: Bool = false): Image { return _create3(width, height, depth, format == null ? TextureFormat.RGBA32 : format, readable, 0); } public static function createRenderTarget(width: Int, height: Int, format: TextureFormat = null, depthStencil: DepthStencilFormat = NoDepthAndStencil, antiAliasingSamples: Int = 1): Image { return _create2(width, height, format == null ? TextureFormat.RGBA32 : format, false, true, depthStencil, antiAliasingSamples); } /** * The provided images need to be readable. */ public static function createArray(images: Array, format: TextureFormat = null): Image { var image = new Image(false); image.myFormat = (format == null) ? TextureFormat.RGBA32 : format; initArrayTexture(image, images); return image; } @:functionCode(" kinc_image_t *kincImages = (kinc_image_t*)malloc(sizeof(kinc_image_t) * images->length); for (unsigned i = 0; i < images->length; ++i) { kinc_image_init(&kincImages[i], images->__get(i).StaticCast<::kha::Image>()->imageData, images->__get(i).StaticCast<::kha::Image>()->originalWidth, images->__get(i).StaticCast<::kha::Image>()->originalHeight, (kinc_image_format_t)getTextureFormat(images->__get(i).StaticCast<::kha::Image>()->myFormat)); } kinc_g4_texture_array_init(&source->textureArray, kincImages, images->length); for (unsigned i = 0; i < images->length; ++i) { kinc_image_destroy(&kincImages[i]); } free(kincImages); ") static function initArrayTexture(source: Image, images: Array): Void {} public static function fromBytes(bytes: Bytes, width: Int, height: Int, format: TextureFormat = null, usage: Usage = null, readable: Bool = false): Image { var image = new Image(readable); image.myFormat = format; image.initFromBytes(bytes.getData(), width, height, getTextureFormat(format)); return image; } @:functionCode(" kinc_image_t image; kinc_image_init(&image, bytes.GetPtr()->GetBase(), width, height, (kinc_image_format_t)format); kinc_g4_texture_init_from_image(&texture, &image); if (readable) { imageData = (uint8_t*)image.data; } kinc_image_destroy(&image); imageType = KhaImageTypeTexture; originalWidth = width; originalHeight = height; ") function initFromBytes(bytes: BytesData, width: Int, height: Int, format: Int): Void {} public static function fromBytes3D(bytes: Bytes, width: Int, height: Int, depth: Int, format: TextureFormat = null, usage: Usage = null, readable: Bool = false): Image { var image = new Image(readable); image.myFormat = format; image.initFromBytes3D(bytes.getData(), width, height, depth, getTextureFormat(format)); return image; } @:functionCode(" kinc_image_t image; kinc_image_init3d(&image, bytes.GetPtr()->GetBase(), width, height, depth, (kinc_image_format_t)format); kinc_g4_texture_init_from_image3d(&texture, &image); if (readable) { imageData = (uint8_t*)image.data; } kinc_image_destroy(&image); imageType = KhaImageTypeTexture; originalWidth = width; originalHeight = height; ") function initFromBytes3D(bytes: BytesData, width: Int, height: Int, depth: Int, format: Int): Void {} public static function fromEncodedBytes(bytes: Bytes, format: String, doneCallback: Image->Void, errorCallback: String->Void, readable: Bool = false): Void { var image = new Image(readable); var isFloat = format == "hdr" || format == "HDR"; image.myFormat = isFloat ? TextureFormat.RGBA128 : TextureFormat.RGBA32; image.initFromEncodedBytes(bytes.getData(), format); doneCallback(image); } @:functionCode(" size_t size = kinc_image_size_from_encoded_bytes(bytes.GetPtr()->GetBase(), bytes.GetPtr()->length, format.c_str()); void* data = malloc(size); kinc_image_t image; kinc_image_init_from_encoded_bytes(&image, data, bytes.GetPtr()->GetBase(), bytes.GetPtr()->length, format.c_str()); originalWidth = image.width; originalHeight = image.height; kinc_g4_texture_init_from_image(&texture, &image); if (readable) { imageData = (uint8_t*)image.data; } kinc_image_destroy(&image); if (!readable) { free(data); } imageType = KhaImageTypeTexture; ") function initFromEncodedBytes(bytes: BytesData, format: String): Void {} function new(readable: Bool, ?dispose = true) { this.readable = readable; nullify(); if (dispose) { cpp.vm.Gc.setFinalizer(this, cpp.Function.fromStaticFunction(finalize)); } } @:functionCode(" imageType = KhaImageTypeNone; originalWidth = 0; originalHeight = 0; imageData = NULL; ownsImageData = false; ") function nullify() {} @:functionCode(" if (image->imageType != KhaImageTypeNone) { image->unload(); } ") @:void static function finalize(image: Image): Void {} static function getRenderTargetFormat(format: TextureFormat): Int { switch (format) { case RGBA32: // Target32Bit return 0; case RGBA64: // Target64BitFloat return 1; case A32: // Target32BitRedFloat return 2; case RGBA128: // Target128BitFloat return 3; case DEPTH16: // Target16BitDepth return 4; case L8: return 5; // Target8BitRed case A16: return 6; // Target16BitRedFloat default: return 0; } } static function getDepthBufferBits(depthAndStencil: DepthStencilFormat): Int { return switch (depthAndStencil) { case NoDepthAndStencil: -1; case DepthOnly: 24; case DepthAutoStencilAuto: 24; case Depth24Stencil8: 24; case Depth32Stencil8: 32; case Depth16: 16; } } static function getStencilBufferBits(depthAndStencil: DepthStencilFormat): Int { return switch (depthAndStencil) { case NoDepthAndStencil: -1; case DepthOnly: -1; case DepthAutoStencilAuto: 8; case Depth24Stencil8: 8; case Depth32Stencil8: 8; case Depth16: 0; } } static function getTextureFormat(format: TextureFormat): Int { switch (format) { case RGBA32: return 0; case RGBA128: return 3; case RGBA64: return 4; case A32: return 5; case A16: return 7; default: return 1; // Grey8 } } @:noCompletion public static function _create2(width: Int, height: Int, format: TextureFormat, readable: Bool, renderTarget: Bool, depthStencil: DepthStencilFormat, samplesPerPixel: Int): Image { var image = new Image(readable); image.myFormat = format; if (renderTarget) image.initRenderTarget(width, height, getRenderTargetFormat(format), getDepthBufferBits(depthStencil), getStencilBufferBits(depthStencil), samplesPerPixel); else image.init(width, height, getTextureFormat(format)); return image; } @:noCompletion public static function _create3(width: Int, height: Int, depth: Int, format: TextureFormat, readable: Bool, contextId: Int): Image { var image = new Image(readable); image.myFormat = format; image.init3D(width, height, depth, getTextureFormat(format)); return image; } @:functionCode(" kinc_g4_render_target_init_with_multisampling(&renderTarget, width, height, (kinc_g4_render_target_format_t)format, depthBufferBits, stencilBufferBits, samplesPerPixel); imageType = KhaImageTypeRenderTarget; originalWidth = width; originalHeight = height; ") function initRenderTarget(width: Int, height: Int, format: Int, depthBufferBits: Int, stencilBufferBits: Int, samplesPerPixel: Int): Void {} @:functionCode(" kinc_g4_texture_init(&texture, width, height, (kinc_image_format_t)format); imageType = KhaImageTypeTexture; originalWidth = width; originalHeight = height; ") function init(width: Int, height: Int, format: Int): Void {} @:functionCode(" kinc_g4_texture_init3d(&texture, width, height, depth, (kinc_image_format_t)format); imageType = KhaImageTypeTexture; originalWidth = width; originalHeight = height; ") function init3D(width: Int, height: Int, depth: Int, format: Int): Void {} @:functionCode(" texture = *kinc_video_current_image(&video->video); imageType = KhaImageTypeTexture; ") function initVideo(video: kha.kore.Video): Void {} public static function createEmpty(readable: Bool, floatFormat: Bool): Image { var image = new Image(readable); image.myFormat = floatFormat ? TextureFormat.RGBA128 : TextureFormat.RGBA32; return image; } /*public static function fromFile(filename: String, readable: Bool): Image { var image = new Image(readable); var isFloat = StringTools.endsWith(filename, ".hdr"); image.format = isFloat ? TextureFormat.RGBA128 : TextureFormat.RGBA32; image.initFromFile(filename); return image; } @:functionCode('texture = new Kore::Graphics4::Texture(filename.c_str(), readable);') private function initFromFile(filename: String): Void { }*/ public var g1(get, never): kha.graphics1.Graphics; function get_g1(): kha.graphics1.Graphics { if (graphics1 == null) { graphics1 = new kha.graphics2.Graphics1(this); } return graphics1; } public var g2(get, never): kha.graphics2.Graphics; function get_g2(): kha.graphics2.Graphics { if (graphics2 == null) { graphics2 = new kha.kore.graphics4.Graphics2(this); } return graphics2; } public var g4(get, never): kha.graphics4.Graphics; function get_g4(): kha.graphics4.Graphics { if (graphics4 == null) { graphics4 = new kha.kore.graphics4.Graphics(this); } return graphics4; } public static var maxSize(get, never): Int; static function get_maxSize(): Int { return 4096; } public static var nonPow2Supported(get, never): Bool; @:functionCode("return kinc_g4_supports_non_pow2_textures();") static function get_nonPow2Supported(): Bool { return false; } @:functionCode("return kinc_g4_render_targets_inverted_y();") public static function renderTargetsInvertedY(): Bool { return false; } public var width(get, never): Int; @:functionCode("return originalWidth;") function get_width(): Int { return 0; } public var height(get, never): Int; @:functionCode("return originalHeight;") function get_height(): Int { return 0; } public var depth(get, never): Int; @:functionCode("if (imageType == KhaImageTypeTexture) return texture.tex_depth; else return 0;") function get_depth(): Int { return 0; } public var format(get, never): TextureFormat; @:functionCode("if (imageType == KhaImageTypeTexture) return texture.format; else return 0;") function get_format(): TextureFormat { return TextureFormat.RGBA32; } public var realWidth(get, never): Int; @:functionCode("if (imageType == KhaImageTypeTexture) return texture.tex_width; else if (imageType == KhaImageTypeRenderTarget) return renderTarget.width; else return 0;") function get_realWidth(): Int { return 0; } public var realHeight(get, never): Int; @:functionCode("if (imageType == KhaImageTypeTexture) return texture.tex_height; else if (imageType == KhaImageTypeRenderTarget) return renderTarget.height; else return 0;") function get_realHeight(): Int { return 0; } public function isOpaque(x: Int, y: Int): Bool { return isOpaqueInternal(x, y, getTextureFormat(myFormat)); } @:functionCode(" kinc_image_t image; kinc_image_init(&image, imageData, originalWidth, originalHeight, (kinc_image_format_t)format); bool opaque = (kinc_image_at(&image, x, y) & 0xff) != 0; kinc_image_destroy(&image); return opaque; ") function isOpaqueInternal(x: Int, y: Int, format: Int): Bool { return true; } public inline function at(x: Int, y: Int): Color { return Color.fromValue(atInternal(x, y, getTextureFormat(myFormat))); } @:functionCode(" kinc_image_t image; kinc_image_init(&image, imageData, originalWidth, originalHeight, (kinc_image_format_t)format); int value = kinc_image_at(&image, x, y); kinc_image_destroy(&image); return value; ") function atInternal(x: Int, y: Int, format: Int): Int { return 0; } @:keep @:functionCode(" if (imageType == KhaImageTypeTexture) { kinc_g4_texture_destroy(&texture); } else if (imageType == KhaImageTypeRenderTarget) { kinc_g4_render_target_destroy(&renderTarget); } else if (imageType == KhaImageTypeTextureArray) { kinc_g4_texture_array_destroy(&textureArray); } else { assert(false); } if (ownsImageData) { free(imageData); } imageData = NULL; imageType = KhaImageTypeNone; ") public function unload(): Void {} var bytes: Bytes = null; @:functionCode(" int size = kinc_image_format_sizeof(texture.format) * originalWidth * originalHeight; this->bytes = ::haxe::io::Bytes_obj::alloc(size); return this->bytes; ") public function lock(level: Int = 0): Bytes { return null; } @:functionCode(" uint8_t *b = bytes->b->Pointer(); uint8_t *tex = kinc_g4_texture_lock(&texture); int size = kinc_image_format_sizeof(texture.format); int stride = kinc_g4_texture_stride(&texture); for (int y = 0; y < texture.tex_height; ++y) { for (int x = 0; x < texture.tex_width; ++x) { #ifdef KORE_DIRECT3D if (texture.format == KINC_IMAGE_FORMAT_RGBA32) { //RBGA->BGRA tex[y * stride + x * size + 0] = b[(y * originalWidth + x) * size + 2]; tex[y * stride + x * size + 1] = b[(y * originalWidth + x) * size + 1]; tex[y * stride + x * size + 2] = b[(y * originalWidth + x) * size + 0]; tex[y * stride + x * size + 3] = b[(y * originalWidth + x) * size + 3]; } else #endif { for (int i = 0; i < size; ++i) { tex[y * stride + x * size + i] = b[(y * originalWidth + x) * size + i]; } } } } kinc_g4_texture_unlock(&texture); ") public function unlock(): Void { bytes = null; } @:ifFeature("kha.Image.getPixelsInternal") var pixels: Bytes = null; @:ifFeature("kha.Image.getPixelsInternal") var pixelsAllocated: Bool = false; @:functionCode(" if (imageType != KhaImageTypeRenderTarget) return NULL; if (!this->pixelsAllocated) { int size = formatSize * renderTarget.width * renderTarget.height; this->pixels = ::haxe::io::Bytes_obj::alloc(size); this->pixelsAllocated = true; } uint8_t *b = this->pixels->b->Pointer(); kinc_g4_render_target_get_pixels(&renderTarget, b); return this->pixels; ") function getPixelsInternal(formatSize: Int): Bytes { return null; } public function getPixels(): Bytes { return getPixelsInternal(formatByteSize(myFormat)); } static function formatByteSize(format: TextureFormat): Int { return switch (format) { case RGBA32: 4; case L8: 1; case RGBA128: 16; case DEPTH16: 2; case RGBA64: 8; case A32: 4; case A16: 2; default: 4; } } public function generateMipmaps(levels: Int): Void { untyped __cpp__("if (imageType == KhaImageTypeTexture) kinc_g4_texture_generate_mipmaps(&texture, levels); else if (imageType == KhaImageTypeRenderTarget) kinc_g4_render_target_generate_mipmaps(&renderTarget, levels)"); } public function setMipmaps(mipmaps: Array): Void { for (i in 0...mipmaps.length) { var khaImage = mipmaps[i]; var level = i + 1; var format = getTextureFormat(this.format); untyped __cpp__(" kinc_image_t image; kinc_image_init(&image, {0}->imageData, {0}->originalWidth, {0}->originalHeight, (kinc_image_format_t){2}); kinc_g4_texture_set_mipmap(&texture, &image, {1}); kinc_image_destroy(&image); ", khaImage, level, format); } } public function setDepthStencilFrom(image: Image): Void { untyped __cpp__("kinc_g4_render_target_set_depth_stencil_from(&renderTarget, &image->renderTarget)"); } @:functionCode("if (imageType == KhaImageTypeTexture) kinc_g4_texture_clear(&texture, x, y, z, width, height, depth, color);") public function clear(x: Int, y: Int, z: Int, width: Int, height: Int, depth: Int, color: Color): Void {} public var stride(get, never): Int; @:functionCode("if (imageType == KhaImageTypeTexture) return kinc_g4_texture_stride(&texture); else if (imageType == KhaImageTypeRenderTarget) return formatByteSize(myFormat) * renderTarget.width; else return 0;") function get_stride(): Int { return 0; } }