/* * Copyright (c) 2021, Jesse Buhagiar * Copyright (c) 2021, Stephan Unverwerth * Copyright (c) 2022, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ #include #include namespace GL { Optional GLContext::get_context_parameter(GLenum name) { switch (name) { case GL_ACTIVE_TEXTURE: return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(GL_TEXTURE0 + m_active_texture_unit_index) } }; case GL_ALPHA_BITS: return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(u8) * 8 } }; case GL_ALPHA_TEST: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_alpha_test_enabled } }; case GL_BLEND: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_blend_enabled } }; case GL_BLEND_DST_ALPHA: return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_blend_destination_factor) } }; case GL_BLEND_SRC_ALPHA: return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_blend_source_factor) } }; case GL_BLUE_BITS: return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(u8) * 8 } }; case GL_CLIENT_ACTIVE_TEXTURE: return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(GL_TEXTURE0 + m_client_active_texture) } }; case GL_COLOR_MATERIAL: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_color_material_enabled } }; case GL_COLOR_MATERIAL_FACE: return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_color_material_face) } }; case GL_COLOR_MATERIAL_MODE: return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_color_material_mode) } }; case GL_CURRENT_COLOR: return ContextParameter { .type = GL_DOUBLE, .count = 4, .value = { .double_list = { static_cast(m_current_vertex_color.x()), static_cast(m_current_vertex_color.y()), static_cast(m_current_vertex_color.z()), static_cast(m_current_vertex_color.w()), } } }; case GL_CULL_FACE: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_cull_faces } }; case GL_DEPTH_BITS: return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(float) * 8 } }; case GL_DEPTH_TEST: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_depth_test_enabled } }; case GL_DITHER: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_dither_enabled } }; case GL_DOUBLEBUFFER: return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = true } }; case GL_FOG: { auto fog_enabled = m_rasterizer->options().fog_enabled; return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = fog_enabled } }; } case GL_GREEN_BITS: return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(u8) * 8 } }; case GL_LIGHTING: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_lighting_enabled } }; case GL_LINE_SMOOTH: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_line_smooth } }; case GL_MAX_CLIP_PLANES: return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_device_info.max_clip_planes) } }; case GL_MAX_LIGHTS: return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_device_info.num_lights) } }; case GL_MAX_MODELVIEW_STACK_DEPTH: return ContextParameter { .type = GL_INT, .value = { .integer_value = MODELVIEW_MATRIX_STACK_LIMIT } }; case GL_MAX_PROJECTION_STACK_DEPTH: return ContextParameter { .type = GL_INT, .value = { .integer_value = PROJECTION_MATRIX_STACK_LIMIT } }; case GL_MAX_TEXTURE_LOD_BIAS: return ContextParameter { .type = GL_DOUBLE, .value = { .double_value = static_cast(m_device_info.max_texture_lod_bias) } }; case GL_MAX_TEXTURE_SIZE: return ContextParameter { .type = GL_INT, .value = { .integer_value = 4096 } }; case GL_MAX_TEXTURE_STACK_DEPTH: return ContextParameter { .type = GL_INT, .value = { .integer_value = TEXTURE_MATRIX_STACK_LIMIT } }; case GL_MAX_TEXTURE_UNITS: return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_texture_units.size()) } }; case GL_NORMAL_ARRAY_TYPE: return ContextParameter { .type = GL_INT, .value = { .integer_value = GL_FLOAT } }; case GL_NORMALIZE: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_normalize } }; case GL_PACK_ALIGNMENT: return ContextParameter { .type = GL_INT, .value = { .integer_value = m_pack_alignment } }; case GL_PACK_IMAGE_HEIGHT: return ContextParameter { .type = GL_BOOL, .value = { .integer_value = 0 } }; case GL_PACK_LSB_FIRST: return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = false } }; case GL_PACK_ROW_LENGTH: return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } }; case GL_PACK_SKIP_PIXELS: return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } }; case GL_PACK_SKIP_ROWS: return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } }; case GL_PACK_SWAP_BYTES: return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = false } }; case GL_POINT_SMOOTH: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_point_smooth } }; case GL_POINT_SIZE: return ContextParameter { .type = GL_DOUBLE, .value = { .double_value = static_cast(m_point_size) } }; case GL_POLYGON_OFFSET_FILL: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_depth_offset_enabled } }; case GL_RED_BITS: return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(u8) * 8 } }; case GL_SAMPLE_BUFFERS: return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } }; case GL_SAMPLES: return ContextParameter { .type = GL_INT, .value = { .integer_value = 1 } }; case GL_SCISSOR_BOX: { auto scissor_box = m_rasterizer->options().scissor_box; return ContextParameter { .type = GL_INT, .count = 4, .value = { .integer_list = { scissor_box.x(), scissor_box.y(), scissor_box.width(), scissor_box.height(), } } }; } case GL_SCISSOR_TEST: { auto scissor_enabled = m_rasterizer->options().scissor_enabled; return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = scissor_enabled } }; } case GL_STENCIL_BITS: return ContextParameter { .type = GL_INT, .value = { .integer_value = m_device_info.stencil_bits } }; case GL_STENCIL_CLEAR_VALUE: return ContextParameter { .type = GL_INT, .value = { .integer_value = m_clear_stencil } }; case GL_STENCIL_TEST: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_stencil_test_enabled } }; case GL_TEXTURE_1D: return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = m_active_texture_unit->texture_1d_enabled() } }; case GL_TEXTURE_2D: return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = m_active_texture_unit->texture_2d_enabled() } }; case GL_TEXTURE_3D: return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = m_active_texture_unit->texture_3d_enabled() } }; case GL_TEXTURE_CUBE_MAP: return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = m_active_texture_unit->texture_cube_map_enabled() } }; case GL_TEXTURE_GEN_Q: case GL_TEXTURE_GEN_R: case GL_TEXTURE_GEN_S: case GL_TEXTURE_GEN_T: { auto generation_enabled = texture_coordinate_generation(m_active_texture_unit_index, name).enabled; return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = generation_enabled } }; } case GL_UNPACK_ALIGNMENT: return ContextParameter { .type = GL_INT, .value = { .integer_value = m_unpack_alignment } }; case GL_UNPACK_IMAGE_HEIGHT: return ContextParameter { .type = GL_BOOL, .value = { .integer_value = 0 } }; case GL_UNPACK_LSB_FIRST: return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = false } }; case GL_UNPACK_ROW_LENGTH: return ContextParameter { .type = GL_INT, .value = { .integer_value = m_unpack_row_length } }; case GL_UNPACK_SKIP_PIXELS: return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } }; case GL_UNPACK_SKIP_ROWS: return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } }; case GL_UNPACK_SWAP_BYTES: return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = false } }; case GL_VIEWPORT: return ContextParameter { .type = GL_INT, .count = 4, .value = { .integer_list = { m_viewport.x(), m_viewport.y(), m_viewport.width(), m_viewport.height(), } } }; default: dbgln_if(GL_DEBUG, "get_context_parameter({:#x}): unknown context parameter", name); return {}; } } void GLContext::gl_disable(GLenum capability) { APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_disable, capability); RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); auto rasterizer_options = m_rasterizer->options(); bool update_rasterizer_options = false; switch (capability) { case GL_CLIP_PLANE0: case GL_CLIP_PLANE1: case GL_CLIP_PLANE2: case GL_CLIP_PLANE3: case GL_CLIP_PLANE4: case GL_CLIP_PLANE5: { auto plane_idx = static_cast(capability) - GL_CLIP_PLANE0; m_clip_plane_attributes.enabled &= ~(1 << plane_idx); m_clip_planes_dirty = true; break; } case GL_COLOR_MATERIAL: m_color_material_enabled = false; break; case GL_CULL_FACE: m_cull_faces = false; rasterizer_options.enable_culling = false; update_rasterizer_options = true; break; case GL_DEPTH_TEST: m_depth_test_enabled = false; rasterizer_options.enable_depth_test = false; update_rasterizer_options = true; break; case GL_BLEND: m_blend_enabled = false; rasterizer_options.enable_blending = false; update_rasterizer_options = true; break; case GL_ALPHA_TEST: m_alpha_test_enabled = false; rasterizer_options.enable_alpha_test = false; update_rasterizer_options = true; break; case GL_DITHER: m_dither_enabled = false; break; case GL_FOG: rasterizer_options.fog_enabled = false; update_rasterizer_options = true; break; case GL_LIGHTING: m_lighting_enabled = false; rasterizer_options.lighting_enabled = false; update_rasterizer_options = true; break; case GL_LIGHT0: case GL_LIGHT1: case GL_LIGHT2: case GL_LIGHT3: case GL_LIGHT4: case GL_LIGHT5: case GL_LIGHT6: case GL_LIGHT7: m_light_states.at(capability - GL_LIGHT0).is_enabled = false; m_light_state_is_dirty = true; break; case GL_LINE_SMOOTH: m_line_smooth = false; rasterizer_options.line_smooth = false; update_rasterizer_options = true; break; case GL_NORMALIZE: m_normalize = false; rasterizer_options.normalization_enabled = false; update_rasterizer_options = true; break; case GL_POINT_SMOOTH: m_point_smooth = false; rasterizer_options.point_smooth = false; update_rasterizer_options = true; break; case GL_POLYGON_OFFSET_FILL: m_depth_offset_enabled = false; rasterizer_options.depth_offset_enabled = false; update_rasterizer_options = true; break; case GL_SCISSOR_TEST: rasterizer_options.scissor_enabled = false; update_rasterizer_options = true; break; case GL_STENCIL_TEST: m_stencil_test_enabled = false; rasterizer_options.enable_stencil_test = false; update_rasterizer_options = true; break; case GL_TEXTURE_1D: m_active_texture_unit->set_texture_1d_enabled(false); m_sampler_config_is_dirty = true; m_texture_units_dirty = true; break; case GL_TEXTURE_2D: m_active_texture_unit->set_texture_2d_enabled(false); m_sampler_config_is_dirty = true; m_texture_units_dirty = true; break; case GL_TEXTURE_3D: m_active_texture_unit->set_texture_3d_enabled(false); m_sampler_config_is_dirty = true; m_texture_units_dirty = true; break; case GL_TEXTURE_CUBE_MAP: m_active_texture_unit->set_texture_cube_map_enabled(false); m_sampler_config_is_dirty = true; m_texture_units_dirty = true; break; case GL_TEXTURE_GEN_Q: case GL_TEXTURE_GEN_R: case GL_TEXTURE_GEN_S: case GL_TEXTURE_GEN_T: texture_coordinate_generation(m_active_texture_unit_index, capability).enabled = false; m_texture_units_dirty = true; break; default: dbgln_if(GL_DEBUG, "gl_disable({:#x}): unknown parameter", capability); RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); } if (update_rasterizer_options) m_rasterizer->set_options(rasterizer_options); } void GLContext::gl_disable_client_state(GLenum cap) { RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); switch (cap) { case GL_COLOR_ARRAY: m_client_side_color_array_enabled = false; break; case GL_NORMAL_ARRAY: m_client_side_normal_array_enabled = false; break; case GL_TEXTURE_COORD_ARRAY: m_client_side_texture_coord_array_enabled[m_client_active_texture] = false; break; case GL_VERTEX_ARRAY: m_client_side_vertex_array_enabled = false; break; default: RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); } } void GLContext::gl_enable(GLenum capability) { APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_enable, capability); RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); auto rasterizer_options = m_rasterizer->options(); bool update_rasterizer_options = false; switch (capability) { case GL_CLIP_PLANE0: case GL_CLIP_PLANE1: case GL_CLIP_PLANE2: case GL_CLIP_PLANE3: case GL_CLIP_PLANE4: case GL_CLIP_PLANE5: { auto plane_idx = static_cast(capability) - GL_CLIP_PLANE0; m_clip_plane_attributes.enabled |= (1 << plane_idx); m_clip_planes_dirty = true; break; } case GL_COLOR_MATERIAL: m_color_material_enabled = true; break; case GL_CULL_FACE: m_cull_faces = true; rasterizer_options.enable_culling = true; update_rasterizer_options = true; break; case GL_DEPTH_TEST: m_depth_test_enabled = true; rasterizer_options.enable_depth_test = true; update_rasterizer_options = true; break; case GL_BLEND: m_blend_enabled = true; rasterizer_options.enable_blending = true; update_rasterizer_options = true; break; case GL_ALPHA_TEST: m_alpha_test_enabled = true; rasterizer_options.enable_alpha_test = true; update_rasterizer_options = true; break; case GL_DITHER: m_dither_enabled = true; break; case GL_FOG: rasterizer_options.fog_enabled = true; update_rasterizer_options = true; break; case GL_LIGHTING: m_lighting_enabled = true; rasterizer_options.lighting_enabled = true; update_rasterizer_options = true; break; case GL_LIGHT0: case GL_LIGHT1: case GL_LIGHT2: case GL_LIGHT3: case GL_LIGHT4: case GL_LIGHT5: case GL_LIGHT6: case GL_LIGHT7: m_light_states.at(capability - GL_LIGHT0).is_enabled = true; m_light_state_is_dirty = true; break; case GL_LINE_SMOOTH: m_line_smooth = true; rasterizer_options.line_smooth = true; update_rasterizer_options = true; break; case GL_NORMALIZE: m_normalize = true; rasterizer_options.normalization_enabled = true; update_rasterizer_options = true; break; case GL_POINT_SMOOTH: m_point_smooth = true; rasterizer_options.point_smooth = true; update_rasterizer_options = true; break; case GL_POLYGON_OFFSET_FILL: m_depth_offset_enabled = true; rasterizer_options.depth_offset_enabled = true; update_rasterizer_options = true; break; case GL_SCISSOR_TEST: rasterizer_options.scissor_enabled = true; update_rasterizer_options = true; break; case GL_STENCIL_TEST: m_stencil_test_enabled = true; rasterizer_options.enable_stencil_test = true; update_rasterizer_options = true; break; case GL_TEXTURE_1D: m_active_texture_unit->set_texture_1d_enabled(true); m_sampler_config_is_dirty = true; m_texture_units_dirty = true; break; case GL_TEXTURE_2D: m_active_texture_unit->set_texture_2d_enabled(true); m_sampler_config_is_dirty = true; m_texture_units_dirty = true; break; case GL_TEXTURE_3D: m_active_texture_unit->set_texture_3d_enabled(true); m_sampler_config_is_dirty = true; m_texture_units_dirty = true; break; case GL_TEXTURE_CUBE_MAP: m_active_texture_unit->set_texture_cube_map_enabled(true); m_sampler_config_is_dirty = true; m_texture_units_dirty = true; break; case GL_TEXTURE_GEN_Q: case GL_TEXTURE_GEN_R: case GL_TEXTURE_GEN_S: case GL_TEXTURE_GEN_T: texture_coordinate_generation(m_active_texture_unit_index, capability).enabled = true; m_texture_units_dirty = true; break; default: dbgln_if(GL_DEBUG, "gl_enable({:#x}): unknown parameter", capability); RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); } if (update_rasterizer_options) m_rasterizer->set_options(rasterizer_options); } void GLContext::gl_enable_client_state(GLenum cap) { RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); switch (cap) { case GL_COLOR_ARRAY: m_client_side_color_array_enabled = true; break; case GL_NORMAL_ARRAY: m_client_side_normal_array_enabled = true; break; case GL_TEXTURE_COORD_ARRAY: m_client_side_texture_coord_array_enabled[m_client_active_texture] = true; break; case GL_VERTEX_ARRAY: m_client_side_vertex_array_enabled = true; break; default: RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); } } void GLContext::gl_get_booleanv(GLenum pname, GLboolean* data) { RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); auto optional_parameter = get_context_parameter(pname); RETURN_WITH_ERROR_IF(!optional_parameter.has_value(), GL_INVALID_ENUM); auto parameter = optional_parameter.release_value(); switch (parameter.type) { case GL_BOOL: *data = parameter.value.boolean_value ? GL_TRUE : GL_FALSE; break; case GL_DOUBLE: *data = (parameter.value.double_value == 0.0) ? GL_FALSE : GL_TRUE; break; case GL_INT: *data = (parameter.value.integer_value == 0) ? GL_FALSE : GL_TRUE; break; default: VERIFY_NOT_REACHED(); } } void GLContext::gl_get_doublev(GLenum pname, GLdouble* params) { get_floating_point(pname, params); } template void GLContext::get_floating_point(GLenum pname, T* params) { RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); // Handle matrix retrieval first auto flatten_and_assign_matrix = [¶ms](FloatMatrix4x4 const& matrix) { auto elements = matrix.elements(); for (size_t i = 0; i < 4; ++i) { for (size_t j = 0; j < 4; ++j) { // Return transposed matrix since OpenGL defines them as column-major params[i * 4 + j] = static_cast(elements[j][i]); } } }; switch (pname) { case GL_MODELVIEW_MATRIX: flatten_and_assign_matrix(model_view_matrix()); return; case GL_PROJECTION_MATRIX: flatten_and_assign_matrix(projection_matrix()); return; } // Regular parameters auto optional_parameter = get_context_parameter(pname); RETURN_WITH_ERROR_IF(!optional_parameter.has_value(), GL_INVALID_ENUM); auto parameter = optional_parameter.release_value(); switch (parameter.type) { case GL_BOOL: *params = parameter.value.boolean_value ? GL_TRUE : GL_FALSE; break; case GL_DOUBLE: for (size_t i = 0; i < parameter.count; ++i) params[i] = parameter.value.double_list[i]; break; case GL_INT: for (size_t i = 0; i < parameter.count; ++i) params[i] = parameter.value.integer_list[i]; break; default: VERIFY_NOT_REACHED(); } } void GLContext::gl_get_floatv(GLenum pname, GLfloat* params) { get_floating_point(pname, params); } void GLContext::gl_get_integerv(GLenum pname, GLint* data) { RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); auto optional_parameter = get_context_parameter(pname); RETURN_WITH_ERROR_IF(!optional_parameter.has_value(), GL_INVALID_ENUM); auto parameter = optional_parameter.release_value(); switch (parameter.type) { case GL_BOOL: *data = parameter.value.boolean_value ? GL_TRUE : GL_FALSE; break; case GL_DOUBLE: { double const int_range = static_cast(NumericLimits::max()) - NumericLimits::min(); for (size_t i = 0; i < parameter.count; ++i) { double const result_factor = (clamp(parameter.value.double_list[i], -1.0, 1.0) + 1.0) / 2.0; data[i] = static_cast(NumericLimits::min() + result_factor * int_range); } break; } case GL_INT: for (size_t i = 0; i < parameter.count; ++i) data[i] = parameter.value.integer_list[i]; break; default: VERIFY_NOT_REACHED(); } } GLboolean GLContext::gl_is_enabled(GLenum capability) { RETURN_VALUE_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION, 0); auto optional_parameter = get_context_parameter(capability); RETURN_VALUE_WITH_ERROR_IF(!optional_parameter.has_value(), GL_INVALID_ENUM, 0); auto parameter = optional_parameter.release_value(); RETURN_VALUE_WITH_ERROR_IF(!parameter.is_capability, GL_INVALID_ENUM, 0); return parameter.value.boolean_value; } GPU::PackingSpecification GLContext::get_packing_specification(PackingType packing_type) { // Make use of the fact that the GL_PACK_* and GL_UNPACK_* enum constants are in the exact same order auto const offset = (packing_type == PackingType::Unpack) ? 0 : (GL_PACK_SWAP_BYTES - GL_UNPACK_SWAP_BYTES); auto get_packing_value = [&](GLenum packing_parameter) -> GLint { GLint value; gl_get_integerv(packing_parameter + offset, &value); return value; }; // FIXME: add support for GL_UNPACK_SKIP_PIXELS, GL_UNPACK_SKIP_ROWS and GL_UNPACK_LSB_FIRST GLint byte_alignment { get_packing_value(GL_UNPACK_ALIGNMENT) }; GLint swap_bytes { get_packing_value(GL_UNPACK_SWAP_BYTES) }; GLint depth_stride { get_packing_value(GL_UNPACK_IMAGE_HEIGHT) }; GLint row_stride { get_packing_value(GL_UNPACK_ROW_LENGTH) }; return { .depth_stride = static_cast(depth_stride), .row_stride = static_cast(row_stride), .byte_alignment = static_cast(byte_alignment), .component_bytes_order = swap_bytes == GL_TRUE ? GPU::ComponentBytesOrder::Reversed : GPU::ComponentBytesOrder::Normal, }; } }