@tool extends TextureRect signal shaven(progress: float) signal key_area_shaven(progress: float) @export var brush_width := 9 @export var key_area := Rect2i(): set(val): key_area = val queue_redraw() # points inside radius 9 circle(必须>5) var brush_points: PackedVector2Array var area_size := Vector2.ZERO var image: Image # bit_mask is the bottom var bit_mask_key_area: BitMap var bit_mask := BitMap.new() var key_area_total_pixels := 0 var total_pixels := 0 # 性能优化相关变量 var update_timer := 0.0 var update_interval := 0.02 # 约50fps var pending_update := false func _ready() -> void: if Engine.is_editor_hint(): return brush_points = generate_brush_points(brush_width) if not texture: printerr("texture not found") return area_size = texture.get_size() image = texture.get_image() # 确保图像具有alpha通道 if image.get_format() != Image.FORMAT_RGBA8: image.convert(Image.FORMAT_RGBA8) texture = ImageTexture.create_from_image(image) # 创建 bit_mask bit_mask.create_from_image_alpha(image) total_pixels = bit_mask.get_true_bit_count() # 创建 bit_mask_key_area if key_area.size.x > 0 and key_area.size.y > 0: bit_mask_key_area = BitMap.new() bit_mask_key_area.create(area_size) # clamp key_area key_area.position = key_area.position.clamp(Vector2.ZERO, area_size - Vector2.ONE) key_area.end = key_area.end.clamp(key_area.position, area_size - Vector2.ONE) # 只保留 key_area 区域中的 bit_mask_key_area for y in range(key_area.position.y, key_area.end.y): for x in range(key_area.position.x, key_area.end.x): var pos = Vector2i(x, y) if bit_mask.get_bitv(pos): bit_mask_key_area.set_bitv(pos, true) key_area_total_pixels = bit_mask_key_area.get_true_bit_count() if GlobalConfig.DEBUG: prints("key_area after clamp:", key_area, "area_size:", area_size, "pixels:", key_area_total_pixels) # a solid circle brush, center = (0, 0) func generate_brush_points(radius: int) -> PackedVector2Array: var points := PackedVector2Array() # Top points for x in range(-radius, radius + 1): for y in range(-radius, radius + 1): # If point is within circle radius if x * x + y * y <= radius * radius: points.append(Vector2(x, y)) # mid points radius = int(radius / 3.0) for x in range(-radius, radius + 1): for y in range(-radius, radius + 1): # If point is within circle radius if x * x + y * y <= radius * radius: points.append(Vector2(x, y)) # bottom points points.append(Vector2(0, 0)) points.append(Vector2(1, 0)) points.append(Vector2(0, 1)) points.append(Vector2(1, 1)) return points var last_pos := Vector2(-1, -1) func _process(delta: float) -> void: if pending_update: update_timer += delta if update_timer >= update_interval: _perform_texture_update() update_timer = 0.0 func _shave() -> void: if not mouse_pressing: return var mouse_pos := get_local_mouse_position() # 添加距离检查,避免重复处理相同位置 if last_pos.distance_to(mouse_pos) < 1.0: return last_pos = mouse_pos var updated = false # 批量处理像素 for point in brush_points: var pos = mouse_pos + point if pos.x < 0 or pos.x >= area_size.x or pos.y < 0 or pos.y >= area_size.y: continue if bit_mask.get_bitv(pos): updated = true var color = image.get_pixelv(pos) color.a = clampf(color.a - 0.15, 0.0, 1.0) if color.a == 0.0: bit_mask.set_bitv(pos, false) if bit_mask_key_area: bit_mask_key_area.set_bitv(pos, false) image.set_pixelv(pos, color) if updated: pending_update = true func _perform_texture_update() -> void: if pending_update: (texture as ImageTexture).update(image) _send_signal() pending_update = false func _send_signal() -> void: if total_pixels > 0: var true_bit_count = bit_mask.get_true_bit_count() var progress = 1.0 - float(true_bit_count) / total_pixels shaven.emit(progress) if key_area_total_pixels > 0: var key_area_true_bit_count = bit_mask_key_area.get_true_bit_count() var key_area_progress = 1.0 - float(key_area_true_bit_count) / key_area_total_pixels key_area_shaven.emit(key_area_progress) var mouse_pressing = false # 可以刮出区域 func _gui_input(event: InputEvent) -> void: if Engine.is_editor_hint(): return if not is_visible_in_tree(): return if event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: mouse_pressing = true # 重置位置以确保首次点击生效 last_pos = Vector2(-1, -1) _shave() elif event.button_index == MOUSE_BUTTON_LEFT and not event.pressed: mouse_pressing = false # 立即执行最后的更新 if pending_update: _perform_texture_update() accept_event() elif event is InputEventMouseMotion and mouse_pressing: _shave() accept_event() func _draw() -> void: # draw key area if exists if Engine.is_editor_hint() and key_area.size.x > 0 and key_area.size.y > 0: draw_rect(key_area, Color.RED, false, 2.0)