// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
fn transform_image_point(
base_x : Double,
base_y : Double,
transform : @smath.Transform,
x : Double,
y : Double,
) -> (Float, Float) {
let world_x = base_x + transform.a * x + transform.c * y + transform.tx
let world_y = base_y + transform.b * x + transform.d * y + transform.ty
(to_float(world_x), to_float(world_y))
}
///|
fn transform_uv_point(
transform : @smath.Transform,
u : Double,
v : Double,
) -> (Float, Float) {
let uv = transform.apply_to_vec2(Vec2(u, v))
(to_float(uv[X]), to_float(uv[Y]))
}
///|
fn can_use_texture_pro_path(transform : @smath.Transform) -> Bool {
transform.b.abs() < 0.000001 && transform.c.abs() < 0.000001
}
///|
fn uv_transform_is_identity(transform : @smath.Transform) -> Bool {
(transform.a - 1.0).abs() < 0.000001 &&
transform.b.abs() < 0.000001 &&
transform.c.abs() < 0.000001 &&
(transform.d - 1.0).abs() < 0.000001 &&
transform.tx.abs() < 0.000001 &&
transform.ty.abs() < 0.000001
}
///|
fn draw_axis_aligned_textured_quad(
texture : @raylib.Texture,
src_x0 : Double,
src_y0 : Double,
src_x1 : Double,
src_y1 : Double,
local_x0 : Double,
local_y0 : Double,
local_x1 : Double,
local_y1 : Double,
base_x : Double,
base_y : Double,
transform : @smath.Transform,
tint : @render.Color,
) -> Unit {
let sx = transform.a
let sy = transform.d
if sx.abs() < 0.000001 || sy.abs() < 0.000001 {
return
}
let x0 = base_x + transform.tx + sx * local_x0
let y0 = base_y + transform.ty + sy * local_y0
let x1 = base_x + transform.tx + sx * local_x1
let y1 = base_y + transform.ty + sy * local_y1
let dest_x = @cmp.minimum(x0, x1)
let dest_y = @cmp.minimum(y0, y1)
let dest_w = (x1 - x0).abs()
let dest_h = (y1 - y0).abs()
if dest_w <= 0.0 || dest_h <= 0.0 {
return
}
let src_w = src_x1 - src_x0
let src_h = src_y1 - src_y0
// DrawTexturePro expects source.x to stay at the original left edge even when
// source.width is negative (it applies horizontal flip internally).
let src_x = src_x0
let src_y = if sy >= 0.0 { src_y0 } else { src_y1 }
let draw_src_w = if sx >= 0.0 { src_w } else { -src_w }
let draw_src_h = if sy >= 0.0 { src_h } else { -src_h }
@raylib.draw_texture_pro(
texture,
to_ray_rect(src_x, src_y, draw_src_w, draw_src_h),
to_ray_rect(dest_x, dest_y, dest_w, dest_h),
to_ray_vector2(0.0, 0.0),
0.0,
to_ray_color(tint),
)
}
///|
fn draw_affine_textured_quad(
texture : @raylib.Texture,
uv00 : (Float, Float),
uv10 : (Float, Float),
uv11 : (Float, Float),
uv01 : (Float, Float),
local_x0 : Double,
local_y0 : Double,
local_x1 : Double,
local_y1 : Double,
base_x : Double,
base_y : Double,
transform : @smath.Transform,
tint : @render.Color,
) -> Unit {
if local_x1 <= local_x0 || local_y1 <= local_y0 {
return
}
let p00 = transform_image_point(base_x, base_y, transform, local_x0, local_y0)
let p10 = transform_image_point(base_x, base_y, transform, local_x1, local_y0)
let p11 = transform_image_point(base_x, base_y, transform, local_x1, local_y1)
let p01 = transform_image_point(base_x, base_y, transform, local_x0, local_y1)
ignore(@rl.check_render_batch_limit(4))
@rl.set_texture(@raylib.get_texture_id(texture))
@rl.begin(@rl.Quads)
let (r, g, b, a) = to_ray_color_bytes(tint)
@rl.color4ub(r, g, b, a)
@rl.normal3f(0.0, 0.0, 1.0)
@rl.tex_coord2f(uv00.0, uv00.1)
@rl.vertex2f(p00.0, p00.1)
@rl.tex_coord2f(uv01.0, uv01.1)
@rl.vertex2f(p01.0, p01.1)
@rl.tex_coord2f(uv11.0, uv11.1)
@rl.vertex2f(p11.0, p11.1)
@rl.tex_coord2f(uv10.0, uv10.1)
@rl.vertex2f(p10.0, p10.1)
@rl.end_()
@rl.set_texture(0U)
}
///|
fn draw_textured_quad(
texture : @raylib.Texture,
texture_width : Double,
texture_height : Double,
src_x0 : Double,
src_y0 : Double,
src_x1 : Double,
src_y1 : Double,
local_x0 : Double,
local_y0 : Double,
local_x1 : Double,
local_y1 : Double,
base_x : Double,
base_y : Double,
transform : @smath.Transform,
uv_transform : @smath.Transform,
tint : @render.Color,
flip_y? : Bool = false,
) -> Unit {
let base_u0 = src_x0 / texture_width
let base_v0_raw = src_y0 / texture_height
let base_u1 = src_x1 / texture_width
let base_v1_raw = src_y1 / texture_height
let (base_v0, base_v1) = if flip_y {
(1.0 - base_v0_raw, 1.0 - base_v1_raw)
} else {
(base_v0_raw, base_v1_raw)
}
let uv00 = transform_uv_point(uv_transform, base_u0, base_v0)
let uv10 = transform_uv_point(uv_transform, base_u1, base_v0)
let uv11 = transform_uv_point(uv_transform, base_u1, base_v1)
let uv01 = transform_uv_point(uv_transform, base_u0, base_v1)
if can_use_texture_pro_path(transform) &&
uv_transform_is_identity(uv_transform) {
draw_axis_aligned_textured_quad(
texture, src_x0, src_y0, src_x1, src_y1, local_x0, local_y0, local_x1, local_y1,
base_x, base_y, transform, tint,
)
return
}
draw_affine_textured_quad(
texture, uv00, uv10, uv11, uv01, local_x0, local_y0, local_x1, local_y1, base_x,
base_y, transform, tint,
)
}
///|
fn draw_image_repeat_transform(
texture : @raylib.Texture,
command : @render2d_types.ImageDrawCommand2D,
flip_y? : Bool = false,
) -> Unit {
let texture_width = @raylib.get_texture_width(texture).to_double()
let texture_height = @raylib.get_texture_height(texture).to_double()
if texture_width <= 0.0 || texture_height <= 0.0 {
return
}
let (src_x, src_y, src_w, src_h) = match command.source {
Some(source) =>
(source.position[X], source.position[Y], source.size[X], source.size[Y])
None => (0.0, 0.0, texture_width, texture_height)
}
let dest_w = command.destination.size[X]
let dest_h = command.destination.size[Y]
if src_w <= 0.0 || src_h <= 0.0 || dest_w <= 0.0 || dest_h <= 0.0 {
return
}
let base_x = command.destination.position[X]
let base_y = command.destination.position[Y]
if command.repeat is NoRepeat {
draw_textured_quad(
texture,
texture_width,
texture_height,
src_x,
src_y,
src_x + src_w,
src_y + src_h,
0.0,
0.0,
dest_w,
dest_h,
base_x,
base_y,
command.transform,
command.uv_transform,
command.color,
flip_y~,
)
return
}
let tile_w = src_w
let tile_h = src_h
let repeat_x = command.repeat is Repeat || command.repeat is RepeatX
let repeat_y = command.repeat is Repeat || command.repeat is RepeatY
let span_x = if repeat_x { dest_w } else { @cmp.minimum(dest_w, tile_w) }
let span_y = if repeat_y { dest_h } else { @cmp.minimum(dest_h, tile_h) }
let mut ox = 0.0
while ox < span_x {
let mut oy = 0.0
while oy < span_y {
let x0 = ox
let y0 = oy
let x1 = @cmp.minimum(ox + tile_w, span_x)
let y1 = @cmp.minimum(oy + tile_h, span_y)
let local_w = x1 - x0
let local_h = y1 - y0
draw_textured_quad(
texture,
texture_width,
texture_height,
src_x,
src_y,
src_x + local_w,
src_y + local_h,
x0,
y0,
x1,
y1,
base_x,
base_y,
command.transform,
command.uv_transform,
command.color,
flip_y~,
)
oy += tile_h
}
ox += tile_w
}
}