Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/BKE_pbvh_pixels.hh
| /* SPDX-License-Identifier: GPL-2.0-or-later | /* SPDX-License-Identifier: GPL-2.0-or-later | ||||
| * Copyright 2022 Blender Foundation. All rights reserved. */ | * Copyright 2022 Blender Foundation. All rights reserved. */ | ||||
| #pragma once | #pragma once | ||||
| #include <functional> | |||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_math_vector_types.hh" | #include "BLI_math_vector.hh" | ||||
| #include "BLI_rect.h" | #include "BLI_rect.h" | ||||
| #include "BLI_vector.hh" | #include "BLI_vector.hh" | ||||
| #include "DNA_image_types.h" | #include "DNA_image_types.h" | ||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "BKE_image.h" | #include "BKE_image.h" | ||||
| #include "BKE_image_wrappers.hh" | #include "BKE_image_wrappers.hh" | ||||
| ▲ Show 20 Lines • Show All 212 Lines • ▼ Show 20 Lines | if (tile && tile->flags.dirty) { | ||||
| else { | else { | ||||
| BKE_image_partial_update_mark_region( | BKE_image_partial_update_mark_region( | ||||
| &image, image_tile.image_tile, &image_buffer, &tile->dirty_region); | &image, image_tile.image_tile, &image_buffer, &tile->dirty_region); | ||||
| } | } | ||||
| tile->clear_dirty(); | tile->clear_dirty(); | ||||
| } | } | ||||
| } | } | ||||
| void collect_dirty_tiles(Vector<image::TileNumber> &r_dirty_tiles) | |||||
| { | |||||
| for (UDIMTilePixels &tile : tiles) { | |||||
| if (tile.flags.dirty) { | |||||
| r_dirty_tiles.append_non_duplicates(tile.tile_number); | |||||
| } | |||||
| } | |||||
| } | |||||
| void clear_data() | void clear_data() | ||||
| { | { | ||||
| tiles.clear(); | tiles.clear(); | ||||
| uv_primitives.clear(); | uv_primitives.clear(); | ||||
| } | } | ||||
| static void free_func(void *instance) | static void free_func(void *instance) | ||||
| { | { | ||||
| NodeData *node_data = static_cast<NodeData *>(instance); | NodeData *node_data = static_cast<NodeData *>(instance); | ||||
| MEM_delete(node_data); | MEM_delete(node_data); | ||||
| } | } | ||||
| }; | }; | ||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Fix non-manifold edge bleeding. | |||||
| * \{ */ | |||||
| // TODO: move to image wrappers? | |||||
| // TODO: Add compile time checks. | |||||
| template<typename T, int Channels = 4> struct ImageBufferAccessor { | |||||
| // BLI_static_assert(constexpr(std::is_same_v<T, int> || std::is_same_v<T, float>); | |||||
| ImBuf &image_buffer; | |||||
| ImageBufferAccessor(ImBuf &image_buffer) : image_buffer(image_buffer) | |||||
| { | |||||
| } | |||||
| float4 read_pixel(const int2 coordinate) | |||||
| { | |||||
| if constexpr ((std::is_same_v<T, float>)) { | |||||
| int offset = (coordinate.y * image_buffer.x + coordinate.x) * Channels; | |||||
| return float4(&image_buffer.rect_float[offset]); | |||||
| } | |||||
| if constexpr ((std::is_same_v<T, int>)) { | |||||
| int offset = (coordinate.y * image_buffer.x + coordinate.x); | |||||
| float4 result; | |||||
| rgba_uchar_to_float(result, | |||||
| static_cast<uchar *>(static_cast<void *>(&image_buffer.rect[offset]))); | |||||
| return result; | |||||
| } | |||||
| return float4(); | |||||
| } | |||||
| void write_pixel(const int2 coordinate, float4 new_value) | |||||
| { | |||||
| if constexpr ((std::is_same_v<T, float>)) { | |||||
| int offset = (coordinate.y * image_buffer.x + coordinate.x) * Channels; | |||||
| copy_v4_v4(&image_buffer.rect_float[offset], new_value); | |||||
| } | |||||
| if constexpr ((std::is_same_v<T, int>)) { | |||||
| int offset = (coordinate.y * image_buffer.x + coordinate.x); | |||||
| rgba_float_to_uchar(static_cast<uchar *>(static_cast<void *>(&image_buffer.rect[offset])), | |||||
| new_value); | |||||
| } | |||||
| } | |||||
| }; | |||||
| struct DeltaCopyPixelCommand { | |||||
| char2 delta_source_1; | |||||
| char2 delta_source_2; | |||||
| uint8_t mix_factor; | |||||
| DeltaCopyPixelCommand(char2 delta_source_1, char2 delta_source_2, uint8_t mix_factor) | |||||
| : delta_source_1(delta_source_1), delta_source_2(delta_source_2), mix_factor(mix_factor) | |||||
| { | |||||
| } | |||||
| }; | |||||
| struct CopyPixelGroup { | |||||
| int2 start_destination; | |||||
| int2 start_source_1; | |||||
| int64_t start_delta_index; | |||||
| int num_deltas; | |||||
| }; | |||||
| /** Pixel copy command to mix 2 source pixels and write to a destination pixel. */ | |||||
| struct CopyPixelCommand { | |||||
| /** Pixel coordinate to write to. */ | |||||
| int2 destination; | |||||
| /** Pixel coordinate to read first source from. */ | |||||
| int2 source_1; | |||||
| /** Pixel coordinate to read second source from. */ | |||||
| int2 source_2; | |||||
| /** Factor to mix between first and second source. */ | |||||
| float mix_factor; | |||||
| CopyPixelCommand() = default; | |||||
| CopyPixelCommand(const CopyPixelGroup &group) | |||||
| : destination(group.start_destination), | |||||
| source_1(group.start_source_1), | |||||
| source_2(), | |||||
| mix_factor(0.0f) | |||||
| { | |||||
| } | |||||
| template<typename T> | |||||
| void mix_source_and_write_destination(ImageBufferAccessor<T> &tile_buffer) const | |||||
| { | |||||
| float4 source_color_1 = tile_buffer.read_pixel(source_1); | |||||
| float4 source_color_2 = tile_buffer.read_pixel(source_2); | |||||
| float4 destination_color = source_color_1 * (1.0f - mix_factor) + source_color_2 * mix_factor; | |||||
| tile_buffer.write_pixel(destination, destination_color); | |||||
| } | |||||
| void apply(const DeltaCopyPixelCommand &item) | |||||
| { | |||||
| destination.x += 1; | |||||
| source_1 += int2(item.delta_source_1); | |||||
| source_2 = source_1 + int2(item.delta_source_2); | |||||
| mix_factor = float(item.mix_factor) / 255.0f; | |||||
| } | |||||
| DeltaCopyPixelCommand encode_delta(const CopyPixelCommand &next_command) const | |||||
| { | |||||
| return DeltaCopyPixelCommand(char2(next_command.source_1 - source_1), | |||||
| char2(next_command.source_2 - next_command.source_1), | |||||
| uint8_t(next_command.mix_factor * 255)); | |||||
| } | |||||
| bool can_be_extended(const CopyPixelCommand &command) const | |||||
| { | |||||
| /* Can only extend sequential pixels. */ | |||||
| if (destination.x != command.destination.x - 1 || destination.y != command.destination.y) { | |||||
| return false; | |||||
| } | |||||
| /* Can only extend when the delta between with the previous source fits in a single byte.*/ | |||||
| int2 delta_source_1 = source_1 - command.source_1; | |||||
| if (max_ii(UNPACK2(blender::math::abs(delta_source_1))) > 127) { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| }; | |||||
| struct CopyPixelTile { | |||||
| image::TileNumber tile_number; | |||||
| Vector<CopyPixelGroup> groups; | |||||
| Vector<DeltaCopyPixelCommand> command_deltas; | |||||
| CopyPixelTile(image::TileNumber tile_number) : tile_number(tile_number) | |||||
| { | |||||
| } | |||||
| void copy_pixels(ImBuf &tile_buffer, IndexRange group_range) const | |||||
| { | |||||
| if (tile_buffer.rect_float) { | |||||
| ImageBufferAccessor<float4> accessor(tile_buffer); | |||||
| copy_pixels<float4>(accessor, group_range); | |||||
| } | |||||
| else { | |||||
| ImageBufferAccessor<int> accessor(tile_buffer); | |||||
| copy_pixels<int>(accessor, group_range); | |||||
| } | |||||
| } | |||||
| void print_compression_rate() | |||||
| { | |||||
| int decoded_size = command_deltas.size() * sizeof(CopyPixelCommand); | |||||
| int encoded_size = groups.size() * sizeof(CopyPixelGroup) + | |||||
| command_deltas.size() * sizeof(DeltaCopyPixelCommand); | |||||
| printf("Tile %d compression rate: %d->%d = %d%%\n", | |||||
| tile_number, | |||||
| decoded_size, | |||||
| encoded_size, | |||||
| int(100.0 * float(encoded_size) / float(decoded_size))); | |||||
| } | |||||
| private: | |||||
| template<typename T> | |||||
| void copy_pixels(ImageBufferAccessor<T> &image_buffer, IndexRange group_range) const | |||||
| { | |||||
| for (const int64_t group_index : group_range) { | |||||
| const CopyPixelGroup &group = groups[group_index]; | |||||
| CopyPixelCommand copy_command(group); | |||||
| for (const DeltaCopyPixelCommand &item : Span<const DeltaCopyPixelCommand>( | |||||
| &command_deltas[group.start_delta_index], group.num_deltas)) { | |||||
| copy_command.apply(item); | |||||
| /* | |||||
| printf("| %d,%d | %d,%d | %d,%d | %f |\n", | |||||
| UNPACK2(copy_command.destination), | |||||
| UNPACK2(copy_command.source_1), | |||||
| UNPACK2(copy_command.source_2), | |||||
| copy_command.mix_factor); | |||||
| */ | |||||
| copy_command.mix_source_and_write_destination<T>(image_buffer); | |||||
| } | |||||
| } | |||||
| } | |||||
| }; | |||||
| struct CopyPixelTiles { | |||||
| Vector<CopyPixelTile> tiles; | |||||
| std::optional<std::reference_wrapper<CopyPixelTile>> find_tile(image::TileNumber tile_number) | |||||
| { | |||||
| for (CopyPixelTile &tile : tiles) { | |||||
| if (tile.tile_number == tile_number) { | |||||
| return tile; | |||||
| } | |||||
| } | |||||
| return std::nullopt; | |||||
| } | |||||
| void clear() | |||||
| { | |||||
| tiles.clear(); | |||||
| } | |||||
| }; | |||||
| /** \} */ | |||||
| struct PBVHData { | struct PBVHData { | ||||
| /* Per UVPRimitive contains the paint data. */ | /* Per UVPRimitive contains the paint data. */ | ||||
| PaintGeometryPrimitives geom_primitives; | PaintGeometryPrimitives geom_primitives; | ||||
| /** Per ImageTile the pixels to copy to fix non-manifold bleeding. */ | |||||
| CopyPixelTiles tiles_copy_pixels; | |||||
| void clear_data() | void clear_data() | ||||
| { | { | ||||
| geom_primitives.clear(); | geom_primitives.clear(); | ||||
| } | } | ||||
| }; | }; | ||||
| NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node); | NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node); | ||||
| void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user); | void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user); | ||||
| PBVHData &BKE_pbvh_pixels_data_get(PBVH &pbvh); | PBVHData &BKE_pbvh_pixels_data_get(PBVH &pbvh); | ||||
| void BKE_pbvh_pixels_collect_dirty_tiles(PBVHNode &node, Vector<image::TileNumber> &r_dirty_tiles); | |||||
| void BKE_pbvh_pixels_copy_pixels(PBVH &pbvh, | |||||
| Image &image, | |||||
| ImageUser &image_user, | |||||
| image::TileNumber tile_number); | |||||
| } // namespace blender::bke::pbvh::pixels | } // namespace blender::bke::pbvh::pixels | ||||