Repository: godotengine/godot
File: editor/plugins/sprite_frames_editor_plugin.cpp (C++)

Code before:

	bool new_texture = texture != split_sheet_preview->get_texture();
	split_sheet_preview->set_texture(texture);
	if (new_texture) {
		// Reset spin max.
		const Size2i size = texture->get_size();
		split_sheet_size_x->set_max(size.x);
		split_sheet_size_y->set_max(size.y);
		split_sheet_sep_x->set_max(size.x);
		split_sheet_sep_y->set_max(size.y);
		split_sheet_offset_x->set_max(size.x);
		split_sheet_offset_y->set_max(size.y);

		if (size != previous_texture_size) {
			// Different texture, reset to 4x4.
			dominant_param = PARAM_FRAME_COUNT;
			updating_split_settings = true;
			split_sheet_h->set_value(4);
			split_sheet_v->set_value(4);
			split_sheet_size_x->set_value(size.x / 4);
			split_sheet_size_y->set_value(size.y / 4);
			split_sheet_sep_x->set_value(0);
			split_sheet_sep_y->set_value(0);
			split_sheet_offset_x->set_value(0);
			split_sheet_offset_y->set_value(0);
			updating_split_settings = false;
		}
		previous_texture_size = size;

		// Reset zoom.
		_sheet_zoom_reset();
	}

	split_sheet_dialog->popup_centered_ratio(0.65);
}

void SpriteFramesEditor::_notification(int p_what) {
	switch (p_what) {
		case NOTIFICATION_ENTER_TREE: {
			get_tree()->connect("node_removed", callable_mp(this, &SpriteFramesEditor::_node_removed));

			[[fallthrough]];
		}
		case NOTIFICATION_THEME_CHANGED: {
			autoplay_icon = get_editor_theme_icon(SNAME("AutoPlay"));
			stop_icon = get_editor_theme_icon(SNAME("Stop"));
			pause_icon = get_editor_theme_icon(SNAME("Pause"));
			_update_stop_icon();

			autoplay->set_icon(get_editor_theme_icon(SNAME("AutoPlay")));
			anim_loop->set_icon(get_editor_theme_icon(SNAME("Loop")));

Review comment (suggestion):
(same in the body)
```suggestion
Size2i SpriteFramesEditor::_estimate_sprite_sheet_size(const Ref<Texture2D> p_texture) {
```

Code after:
	split_sheet_h->set_value(split_sheet.x);
	split_sheet_v->set_value(split_sheet.y);
	split_sheet_size_x->set_value(size.x / split_sheet.x);
	split_sheet_size_y->set_value(size.y / split_sheet.y);
	split_sheet_sep_x->set_value(0);
	split_sheet_sep_y->set_value(0);
	split_sheet_offset_x->set_value(0);
	split_sheet_offset_y->set_value(0);

	updating_split_settings = false;

	frames_selected.clear();
	selected_count = 0;
	last_frame_selected = -1;
	split_sheet_preview->queue_redraw();
}

bool SpriteFramesEditor::_matches_background_color(const Color &p_background_color, const Color &p_pixel_color) {
	if ((p_background_color.a == 0 && p_pixel_color.a == 0) || p_background_color.is_equal_approx(p_pixel_color)) {
		return true;
	}

	Color d = p_background_color - p_pixel_color;
	// 0.04f is the threshold for how much a colour can deviate from background colour and still be considered a match. Arrived at through experimentation, can be tweaked.
	return (d.r * d.r) + (d.g * d.g) + (d.b * d.b) + (d.a * d.a) < 0.04f;
}

Size2i SpriteFramesEditor::_estimate_sprite_sheet_size(const Ref<Texture2D> p_texture) {
	Ref<Image> image = p_texture->get_image();
	Size2i size = p_texture->get_size();

	Color assumed_background_color = image->get_pixel(0, 0);
	Size2i sheet_size;

	bool previous_line_background = true;
	for (int x = 0; x < size.x; x++) {
		int y = 0;
		while (y < size.y && _matches_background_color(assumed_background_color, image->get_pixel(x, y))) {
			y++;
		}
		bool current_line_background = (y == size.y);
		if (previous_line_background && !current_line_background) {
			sheet_size.x++;
		}
		previous_line_background = current_line_background;
	}

	previous_line_background = true;
	for (int y = 0; y < size.y; y++) {
		int x = 0;
		while (x < size.x && _matches_background_color(assumed_background_color, image->get_pixel(x, y))) {