Move API-agnostic image procs out of texture
, to prepare to add threads.
* Procs `loadFileFromPointersLen`, `getDimensionsFormat` and related graphics API-agnostic functions have been moved into their own module `texture_decode`. * New directory `gpu_formats` added, where we will put all modules related to decoding, optimizing and caching meshes and textures (independently of the graphics API being used) in a separate thread or threads.
This commit is contained in:
parent
78cafe6566
commit
49d919f43d
200
src/gpu_formats/texture_decode.nim
Normal file
200
src/gpu_formats/texture_decode.nim
Normal file
|
@ -0,0 +1,200 @@
|
|||
# The contents of this file are subject to the Common Public Attribution License
|
||||
# Version 1.0 (the “License”); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
# https://myou.dev/licenses/LICENSE-CPAL. The License is based on the Mozilla
|
||||
# Public License Version 1.1 but Sections 14 and 15 have been added to cover use
|
||||
# of software over a computer network and provide for limited attribution for
|
||||
# the Original Developer. In addition, Exhibit A has been modified to be
|
||||
# consistent with Exhibit B.
|
||||
#
|
||||
# Software distributed under the License is distributed on an “AS IS” basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
# the specific language governing rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is Myou Engine.
|
||||
#
|
||||
# the Original Developer is the Initial Developer.
|
||||
#
|
||||
# The Initial Developer of the Original Code is the Myou Engine developers.
|
||||
# All portions of the code written by the Myou Engine developers are Copyright
|
||||
# (c) 2024. All Rights Reserved.
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of the
|
||||
# GNU Affero General Public License version 3 (the [AGPL-3] License), in which
|
||||
# case the provisions of [AGPL-3] License are applicable instead of those above.
|
||||
#
|
||||
# If you wish to allow use of your version of this file only under the terms of
|
||||
# the [AGPL-3] License and not to allow others to use your version of this file
|
||||
# under the CPAL, indicate your decision by deleting the provisions above and
|
||||
# replace them with the notice and other provisions required by the [AGPL-3]
|
||||
# License. If you do not delete the provisions above, a recipient may use your
|
||||
# version of this file under either the CPAL or the [AGPL-3] License.
|
||||
|
||||
import ../types
|
||||
|
||||
# Forward declarations
|
||||
proc swap_lines(p: pointer, line_stride, line_count: int)
|
||||
# End forward declarations
|
||||
|
||||
import std/strformat
|
||||
import vmath except Quat
|
||||
import arr_ref
|
||||
import float16
|
||||
|
||||
when defined(myouUsePixie):
|
||||
import pixie
|
||||
else:
|
||||
import stb_image/read as stbi
|
||||
const myouConvertHdrToFloat16 {.booldefine.} = true
|
||||
|
||||
when not defined(nimdoc):
|
||||
import tinyexr
|
||||
|
||||
proc stride*(format: TextureFormat): int =
|
||||
case format:
|
||||
of SRGB_u8: 3
|
||||
of SRGB_Alpha_u8: 4
|
||||
of R_u8: 1
|
||||
of RG_u8: 2
|
||||
of RGB_u8: 3
|
||||
of RGBA_u8: 4
|
||||
of R_u16, R_f16: 2
|
||||
of RG_u16, RG_f16: 4
|
||||
of RGB_u16, RGB_f16: 6
|
||||
of RGBA_u16, RGBA_f16: 8
|
||||
of R_f32: 4
|
||||
of RG_f32: 8
|
||||
of RGB_f32: 12
|
||||
of RGBA_f32: 16
|
||||
of Depth_u16: 2
|
||||
of Depth_u24: 3 # TODO: are you sure?
|
||||
# of Depth_u24_s8: 4
|
||||
of Depth_f32: 4
|
||||
|
||||
proc channel_count*(format: TextureFormat): int =
|
||||
case format:
|
||||
of R_u8, R_u16, R_f16, R_f32, Depth_u16, Depth_u24, Depth_f32: 1
|
||||
of RG_u8, RG_u16, RG_f16, RG_f32: 2
|
||||
of SRGB_u8, RGB_u8, RGB_u16, RGB_f16, RGB_f32: 3
|
||||
else: 4
|
||||
|
||||
template toOpenArrayByte(p: pointer, a,b: untyped): untyped =
|
||||
cast[ptr UncheckedArray[byte]](p).toOpenArray(a,b)
|
||||
|
||||
# template toOpenArray[T](p: pointer, a,b: untyped): untyped =
|
||||
# cast[ptr UncheckedArray[T]](p).toOpenArray(a,b)
|
||||
|
||||
proc f32_to_f16(source: ptr UncheckedArray[float32], dest: ptr UncheckedArray[Float16], len: int) =
|
||||
for i in 0 ..< len:
|
||||
dest[i] = source[i].tofloat16(clamp=true)
|
||||
|
||||
proc getDimensionsFormat*(p: pointer, len: int): (int, int, TextureFormat) =
|
||||
when not defined(nimdoc):
|
||||
if isEXR(p, len):
|
||||
let dims = getEXRDimensions(p, len)
|
||||
return (dims[0], dims[1], RGBA_f16)
|
||||
when defined(myouUsePixie):
|
||||
let dims = decodeImageDimensions(p, len)
|
||||
return (dims.width, dims.height, RGBA_u8)
|
||||
else:
|
||||
var width, height, channels = 0
|
||||
if not infoFromMemory(p.toOpenArrayByte(0, len-1), width, height, channels):
|
||||
raise ValueError.newException "Could not read image"
|
||||
let hdr = isHDRFromMemory(p.toOpenArrayByte(0, len-1))
|
||||
let is16 = is16BitFromMemory(p.toOpenArrayByte(0, len-1))
|
||||
# Calculate format with channels, and whether it's hdr or 16 bit
|
||||
assert (RG_u8.int - R_u8.int) == 1 # (just in case someone changes the enum)
|
||||
const toHDR = when myouConvertHdrToFloat16: (R_f16.int-R_u8.int) else: (R_f32.int-R_u8.int)
|
||||
let format = (R_u8.int - 1 + channels +
|
||||
hdr.int * toHDR + is16.int * (R_u16.int-R_u8.int)).TextureFormat
|
||||
return (width, height, format)
|
||||
|
||||
proc loadFileFromPointersLen*(tex: Texture, pointers: seq[(pointer, int)],
|
||||
callback: proc(tex: Texture, p: pointer), flip = true) =
|
||||
|
||||
when not defined(nimdoc):
|
||||
assert tex.tex_type != TexCube, "Loading a cube texture from file is not supported yet"
|
||||
let layer_stride = tex.width * tex.height * tex.format.stride
|
||||
var multilayer_buffer: ArrRef[byte]
|
||||
assert tex.depth == pointers.len
|
||||
if tex.depth > 1:
|
||||
multilayer_buffer = newArrRef[byte](layer_stride * tex.depth)
|
||||
var pos = 0
|
||||
for (p, len) in pointers:
|
||||
when defined(myouUsePixie):
|
||||
var image: Image
|
||||
else:
|
||||
var image: imagePixelData[byte]
|
||||
var image_16: imagePixelData[uint16]
|
||||
var image_f: imagePixelData[float32]
|
||||
var buffer: ArrRef[byte]
|
||||
# a reference to this pointer is kept with one of the vars above
|
||||
var pixels_ptr: pointer
|
||||
var pixels_len: int
|
||||
var flip = flip
|
||||
if isEXR(p, len):
|
||||
let (width, height, pixels) = decodeEXR(p, len)
|
||||
assert width == tex.width and height == tex.height, "Image size mismatch"
|
||||
buffer = pixels.to byte
|
||||
pixels_ptr = buffer[0].addr
|
||||
pixels_len = buffer.len
|
||||
else:
|
||||
when defined(myouUsePixie):
|
||||
image = decodeImage(p, len)
|
||||
assert image.width == tex.width and image.height == tex.height, "Image size mismatch"
|
||||
pixels_ptr = image.data[0].addr
|
||||
pixels_len = image.data.len * sizeof image.data[0]
|
||||
else:
|
||||
setFlipVerticallyOnLoad(flip)
|
||||
flip = false
|
||||
var w,h,c = 0
|
||||
if isHDRFromMemory(p.toOpenArrayByte(0, len-1)):
|
||||
image_f = loadFFromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
||||
pixels_ptr = image_f.data
|
||||
pixels_len = image_f.byteLen
|
||||
when myouConvertHdrToFloat16:
|
||||
f32_to_f16(
|
||||
cast[ptr UncheckedArray[float32]](pixels_ptr),
|
||||
cast[ptr UncheckedArray[Float16]](pixels_ptr),
|
||||
image_f.len)
|
||||
pixels_len = pixels_len div 2
|
||||
elif is16BitFromMemory(p.toOpenArrayByte(0, len-1)):
|
||||
image_16 = load16FromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
||||
pixels_ptr = image_16.data
|
||||
pixels_len = image_16.byteLen
|
||||
else:
|
||||
image = loadFromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
||||
pixels_ptr = image.data
|
||||
pixels_len = image.len
|
||||
assert layer_stride == pixels_len,
|
||||
&"Image '{tex.name}' has a length of {pixels_len}, expected {layer_stride}"
|
||||
if flip:
|
||||
swap_lines(pixels_ptr, tex.width * tex.format.stride, tex.height)
|
||||
if tex.depth == 1:
|
||||
callback(tex, pixels_ptr)
|
||||
return
|
||||
copyMem(multilayer_buffer[pos].addr, pixels_ptr, layer_stride)
|
||||
pos += layer_stride
|
||||
callback(tex, multilayer_buffer[0].addr)
|
||||
|
||||
|
||||
proc swap_lines(p: pointer, line_stride, line_count: int) =
|
||||
template `+`(p: pointer, i: Natural): pointer = cast[pointer](cast[int](p) +% cast[int](i))
|
||||
template `-`(p: pointer, i: Natural): pointer = cast[pointer](cast[int](p) -% cast[int](i))
|
||||
let int_stride = line_stride div sizeof(int)
|
||||
let int_stride_bytes = int_stride * sizeof(int)
|
||||
var p1 = p
|
||||
var p2 = p + line_stride*(line_count-1)
|
||||
var a1, a2: ptr UncheckedArray[int]
|
||||
var b1, b2: ptr UncheckedArray[byte]
|
||||
for i in 0 ..< line_count div 2:
|
||||
a1 = cast[ptr UncheckedArray[int]](p1)
|
||||
a2 = cast[ptr UncheckedArray[int]](p2)
|
||||
b1 = cast[ptr UncheckedArray[byte]](p1)
|
||||
b2 = cast[ptr UncheckedArray[byte]](p2)
|
||||
for j in 0 ..< int_stride:
|
||||
swap(a1[j], a2[j])
|
||||
for j in int_stride_bytes ..< line_stride:
|
||||
swap(b1[j], b2[j])
|
||||
p1 = p1 + line_stride
|
||||
p2 = p2 - line_stride
|
|
@ -35,11 +35,10 @@ import vmath except Quat
|
|||
import arr_ref
|
||||
# import tinyre
|
||||
import std/tables
|
||||
import ../gpu_formats/texture_decode
|
||||
import ../platform/gl
|
||||
|
||||
# Forward declarations
|
||||
proc stride*(format: TextureFormat): int
|
||||
proc channel_count*(format: TextureFormat): int
|
||||
func mipmapHigh*(self: Texture): int
|
||||
proc needsMipmap*(self: Texture): bool
|
||||
proc setMaxTextures*(count: int32)
|
||||
|
@ -52,7 +51,6 @@ proc unbindAllTextures*()
|
|||
proc destroy*(texture: Texture)
|
||||
proc loadFromPixels*(self: Texture, pixels: pointer)
|
||||
proc loadCubeSideFromPixels*(self: Texture, pixels: pointer, side: int32 = 0)
|
||||
proc loadFileFromPointersLen*(self: Texture, pointers: seq[(pointer, int)], flip = true)
|
||||
proc setFilter*(self: Texture, filter: TextureFilter)
|
||||
proc newTexture*(engine: MyouEngine, name: string, width, height: int, depth: int = 1,
|
||||
format: TextureFormat,
|
||||
|
@ -60,7 +58,6 @@ proc newTexture*(engine: MyouEngine, name: string, width, height: int, depth: in
|
|||
filter: TextureFilter = Trilinear,
|
||||
pixels: ArrRef[float32] = nil): Texture
|
||||
proc generateMipmap*(self: Texture)
|
||||
proc getDimensionsFormat(p: pointer, len: int): (int, int, TextureFormat)
|
||||
func to_sRGB*(format: TextureFormat): TextureFormat
|
||||
proc newTexture*(engine: MyouEngine, name: string, p: pointer, len: int, is_sRGB: bool, filter: TextureFilter = Trilinear, depth=1, flip=true): Texture
|
||||
proc newTexture*(engine: MyouEngine, name: string, file_name: string, is_sRGB: bool,
|
||||
|
@ -69,7 +66,6 @@ proc newTexture*(engine: MyouEngine, name: string, file_name: string, is_sRGB: b
|
|||
flip = true,
|
||||
): Texture
|
||||
proc setExtrapolation*(self: Texture, ext: TextureExtrapolation)
|
||||
proc swap_lines(p: pointer, line_stride, line_count: int)
|
||||
proc getTexturePixels*(self: Texture): TexturePixels
|
||||
proc setMipmapRange*(self: Texture, first = 0, last = 1000)
|
||||
func vec3size*(self: Texture, mip_level = -1): Vec3
|
||||
|
@ -78,18 +74,8 @@ func toInternalFormat*(format: TextureFormat): GLenum
|
|||
|
||||
# import sugar
|
||||
|
||||
when defined(myouUsePixie):
|
||||
import pixie
|
||||
else:
|
||||
import stb_image/read as stbi
|
||||
const myouConvertHdrToFloat16 {.booldefine.} = true
|
||||
|
||||
when not defined(nimdoc):
|
||||
import tinyexr
|
||||
|
||||
import std/bitops
|
||||
import std/strformat
|
||||
import float16
|
||||
import loadable
|
||||
import ddx_ktx
|
||||
|
||||
|
@ -110,34 +96,6 @@ if defined(android):
|
|||
# even if it's not used, we should test it
|
||||
reserved = 1
|
||||
|
||||
proc stride*(format: TextureFormat): int =
|
||||
case format:
|
||||
of SRGB_u8: 3
|
||||
of SRGB_Alpha_u8: 4
|
||||
of R_u8: 1
|
||||
of RG_u8: 2
|
||||
of RGB_u8: 3
|
||||
of RGBA_u8: 4
|
||||
of R_u16, R_f16: 2
|
||||
of RG_u16, RG_f16: 4
|
||||
of RGB_u16, RGB_f16: 6
|
||||
of RGBA_u16, RGBA_f16: 8
|
||||
of R_f32: 4
|
||||
of RG_f32: 8
|
||||
of RGB_f32: 12
|
||||
of RGBA_f32: 16
|
||||
of Depth_u16: 2
|
||||
of Depth_u24: 3 # TODO: are you sure?
|
||||
# of Depth_u24_s8: 4
|
||||
of Depth_f32: 4
|
||||
|
||||
proc channel_count*(format: TextureFormat): int =
|
||||
case format:
|
||||
of R_u8, R_u16, R_f16, R_f32, Depth_u16, Depth_u24, Depth_f32: 1
|
||||
of RG_u8, RG_u16, RG_f16, RG_f32: 2
|
||||
of SRGB_u8, RGB_u8, RGB_u16, RGB_f16, RGB_f32: 3
|
||||
else: 4
|
||||
|
||||
func samplerType*(tex_type: TextureType): string =
|
||||
case tex_type:
|
||||
of Tex2D: "sampler2D"
|
||||
|
@ -409,82 +367,6 @@ proc loadCompressedData*(self: Texture, info: KtxInfo, data: seq[KtxPart]) =
|
|||
info.internal_format.GLenum, self.width.GLsizei, self.height.GLsizei,
|
||||
0, part.len.GLsizei, part.data)
|
||||
|
||||
template toOpenArrayByte(p: pointer, a,b: untyped): untyped =
|
||||
cast[ptr UncheckedArray[byte]](p).toOpenArray(a,b)
|
||||
|
||||
template toOpenArray[T](p: pointer, a,b: untyped): untyped =
|
||||
cast[ptr UncheckedArray[T]](p).toOpenArray(a,b)
|
||||
|
||||
proc f32_to_f16(source: ptr UncheckedArray[float32], dest: ptr UncheckedArray[Float16], len: int) =
|
||||
for i in 0 ..< len:
|
||||
dest[i] = source[i].tofloat16(clamp=true)
|
||||
|
||||
proc loadFileFromPointersLen*(self: Texture, pointers: seq[(pointer, int)], flip = true) =
|
||||
when not defined(nimdoc):
|
||||
assert self.tex_type != TexCube, "Loading a cube texture from file is not supported yet"
|
||||
let layer_stride = self.width * self.height * self.format.stride
|
||||
var multilayer_buffer: ArrRef[byte]
|
||||
assert self.depth == pointers.len
|
||||
if self.depth > 1:
|
||||
multilayer_buffer = newArrRef[byte](layer_stride * self.depth)
|
||||
var pos = 0
|
||||
for (p, len) in pointers:
|
||||
when defined(myouUsePixie):
|
||||
var image: Image
|
||||
else:
|
||||
var image: imagePixelData[byte]
|
||||
var image_16: imagePixelData[uint16]
|
||||
var image_f: imagePixelData[float32]
|
||||
var buffer: ArrRef[byte]
|
||||
# a reference to this pointer is kept with one of the vars above
|
||||
var pixels_ptr: pointer
|
||||
var pixels_len: int
|
||||
var flip = flip
|
||||
if isEXR(p, len):
|
||||
let (width, height, pixels) = decodeEXR(p, len)
|
||||
assert width == self.width and height == self.height, "Image size mismatch"
|
||||
buffer = pixels.to byte
|
||||
pixels_ptr = buffer[0].addr
|
||||
pixels_len = buffer.len
|
||||
else:
|
||||
when defined(myouUsePixie):
|
||||
image = decodeImage(p, len)
|
||||
assert image.width == self.width and image.height == self.height, "Image size mismatch"
|
||||
pixels_ptr = image.data[0].addr
|
||||
pixels_len = image.data.len * sizeof image.data[0]
|
||||
else:
|
||||
setFlipVerticallyOnLoad(flip)
|
||||
flip = false
|
||||
var w,h,c = 0
|
||||
if isHDRFromMemory(p.toOpenArrayByte(0, len-1)):
|
||||
image_f = loadFFromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
||||
pixels_ptr = image_f.data
|
||||
pixels_len = image_f.byteLen
|
||||
when myouConvertHdrToFloat16:
|
||||
f32_to_f16(
|
||||
cast[ptr UncheckedArray[float32]](pixels_ptr),
|
||||
cast[ptr UncheckedArray[Float16]](pixels_ptr),
|
||||
image_f.len)
|
||||
pixels_len = pixels_len div 2
|
||||
elif is16BitFromMemory(p.toOpenArrayByte(0, len-1)):
|
||||
image_16 = load16FromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
||||
pixels_ptr = image_16.data
|
||||
pixels_len = image_16.byteLen
|
||||
else:
|
||||
image = loadFromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
||||
pixels_ptr = image.data
|
||||
pixels_len = image.len
|
||||
assert layer_stride == pixels_len,
|
||||
&"Image '{self.name}' has a length of {pixels_len}, expected {layer_stride}"
|
||||
if flip:
|
||||
swap_lines(pixels_ptr, self.width * self.format.stride, self.height)
|
||||
if self.depth == 1:
|
||||
self.loadFromPixels pixels_ptr
|
||||
return
|
||||
copyMem(multilayer_buffer[pos].addr, pixels_ptr, layer_stride)
|
||||
pos += layer_stride
|
||||
self.loadFromPixels multilayer_buffer[0].addr
|
||||
|
||||
proc setFilter*(self: Texture, filter: TextureFilter) =
|
||||
self.filter = filter
|
||||
self.engine.renderer.enqueue proc()=
|
||||
|
@ -548,27 +430,6 @@ proc generateMipmap*(self: Texture) =
|
|||
self.bind_it(needs_active_texture=true)
|
||||
glGenerateMipmap(self.storage.target)
|
||||
|
||||
proc getDimensionsFormat(p: pointer, len: int): (int, int, TextureFormat) =
|
||||
when not defined(nimdoc):
|
||||
if isEXR(p, len):
|
||||
let dims = getEXRDimensions(p, len)
|
||||
return (dims[0], dims[1], RGBA_f16)
|
||||
when defined(myouUsePixie):
|
||||
let dims = decodeImageDimensions(p, len)
|
||||
return (dims.width, dims.height, RGBA_u8)
|
||||
else:
|
||||
var width, height, channels = 0
|
||||
if not infoFromMemory(p.toOpenArrayByte(0, len-1), width, height, channels):
|
||||
raise ValueError.newException "Could not read image"
|
||||
let hdr = isHDRFromMemory(p.toOpenArrayByte(0, len-1))
|
||||
let is16 = is16BitFromMemory(p.toOpenArrayByte(0, len-1))
|
||||
# Calculate format with channels, and whether it's hdr or 16 bit
|
||||
assert (RG_u8.int - R_u8.int) == 1 # (just in case someone changes the enum)
|
||||
const toHDR = when myouConvertHdrToFloat16: (R_f16.int-R_u8.int) else: (R_f32.int-R_u8.int)
|
||||
let format = (R_u8.int - 1 + channels +
|
||||
hdr.int * toHDR + is16.int * (R_u16.int-R_u8.int)).TextureFormat
|
||||
return (width, height, format)
|
||||
|
||||
func to_sRGB*(format: TextureFormat): TextureFormat =
|
||||
return case format:
|
||||
of RGBA_u8: SRGB_Alpha_u8
|
||||
|
@ -582,7 +443,7 @@ proc newTexture*(engine: MyouEngine, name: string, p: pointer, len: int, is_sRGB
|
|||
format = format.to_sRGB
|
||||
let self = engine.newTexture(name, width, height, depth, format, filter=filter)
|
||||
engine.renderer.enqueue proc() =
|
||||
self.loadFileFromPointersLen(@[(p, len)], flip=flip)
|
||||
self.loadFileFromPointersLen(@[(p, len)], loadFromPixels, flip=flip)
|
||||
self.loaded = true
|
||||
return self
|
||||
|
||||
|
@ -633,7 +494,7 @@ proc newTexture*(engine: MyouEngine, name: string, file_name: string, is_sRGB: b
|
|||
engine.renderer.enqueue proc()=
|
||||
try:
|
||||
self.ensure_storage()
|
||||
self.loadFileFromPointersLen(@[(p, len)])
|
||||
self.loadFileFromPointersLen(@[(p, len)], loadFromPixels)
|
||||
self.loaded = true
|
||||
except:
|
||||
# TODO: use logging
|
||||
|
@ -657,27 +518,6 @@ proc setExtrapolation*(self: Texture, ext: TextureExtrapolation) =
|
|||
glTexParameteri(self.storage.target, GL_TEXTURE_WRAP_T, e.GLint)
|
||||
glTexParameteri(self.storage.target, GL_TEXTURE_WRAP_R, e.GLint)
|
||||
|
||||
proc swap_lines(p: pointer, line_stride, line_count: int) =
|
||||
template `+`(p: pointer, i: Natural): pointer = cast[pointer](cast[int](p) +% cast[int](i))
|
||||
template `-`(p: pointer, i: Natural): pointer = cast[pointer](cast[int](p) -% cast[int](i))
|
||||
let int_stride = line_stride div sizeof(int)
|
||||
let int_stride_bytes = int_stride * sizeof(int)
|
||||
var p1 = p
|
||||
var p2 = p + line_stride*(line_count-1)
|
||||
var a1, a2: ptr UncheckedArray[int]
|
||||
var b1, b2: ptr UncheckedArray[byte]
|
||||
for i in 0 ..< line_count div 2:
|
||||
a1 = cast[ptr UncheckedArray[int]](p1)
|
||||
a2 = cast[ptr UncheckedArray[int]](p2)
|
||||
b1 = cast[ptr UncheckedArray[byte]](p1)
|
||||
b2 = cast[ptr UncheckedArray[byte]](p2)
|
||||
for j in 0 ..< int_stride:
|
||||
swap(a1[j], a2[j])
|
||||
for j in int_stride_bytes ..< line_stride:
|
||||
swap(b1[j], b2[j])
|
||||
p1 = p1 + line_stride
|
||||
p2 = p2 - line_stride
|
||||
|
||||
proc getTexturePixels*(self: Texture): TexturePixels =
|
||||
when false:
|
||||
var len_bytes, cube_stride = self.width * self.height * self.format.stride
|
||||
|
|
Loading…
Reference in a new issue