Initial Grease Pencil 3.0 stage #106848
|
@ -126,8 +126,8 @@ class GHOST_ContextVK : public GHOST_Context {
|
|||
void *image, void *framebuffer, void *render_pass, void *extent, uint32_t *fb_id);
|
||||
|
||||
/**
|
||||
* Sets the swap interval for swapBuffers.
|
||||
* \param interval The swap interval to use.
|
||||
* Sets the swap interval for `swapBuffers`.
|
||||
* \param interval: The swap interval to use.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess setSwapInterval(int /* interval */)
|
||||
|
@ -137,7 +137,7 @@ class GHOST_ContextVK : public GHOST_Context {
|
|||
|
||||
/**
|
||||
* Gets the current swap interval for swapBuffers.
|
||||
* \param intervalOut Variable to store the swap interval if it can be read.
|
||||
* \param intervalOut: Variable to store the swap interval if it can be read.
|
||||
* \return Whether the swap interval can be read.
|
||||
*/
|
||||
GHOST_TSuccess getSwapInterval(int &)
|
||||
|
|
|
@ -376,7 +376,7 @@ bool BKE_fcurve_calc_range(const struct FCurve *fcu,
|
|||
|
||||
/**
|
||||
* Calculate the x and y extents of F-Curve's data.
|
||||
* \param frame_range Only calculate the bounds of the FCurve in the given range.
|
||||
* \param frame_range: Only calculate the bounds of the FCurve in the given range.
|
||||
* Does the full range if NULL.
|
||||
* \return true if the bounds have been found.
|
||||
*/
|
||||
|
|
|
@ -344,7 +344,7 @@ typedef enum eGP_GetFrame_Mode {
|
|||
|
||||
/**
|
||||
* Get the appropriate gp-frame from a given layer
|
||||
* - this sets the layer's actframe var (if allowed to)
|
||||
* - this sets the layer's `actframe` var (if allowed to)
|
||||
* - extension beyond range (if first gp-frame is after all frame in interest and cannot add)
|
||||
*
|
||||
* \param gpl: Grease pencil layer
|
||||
|
@ -523,7 +523,7 @@ struct Material *BKE_gpencil_object_material_new(struct Main *bmain,
|
|||
int *r_index);
|
||||
|
||||
/**
|
||||
* Get material index (0-based like mat_nr not actcol).
|
||||
* Get material index (0-based like mat_nr not #Object::actcol).
|
||||
* \param ob: Grease pencil object
|
||||
* \param ma: Material
|
||||
* \return Index of the material
|
||||
|
|
|
@ -185,7 +185,7 @@ typedef struct Main {
|
|||
ListBase particles;
|
||||
ListBase palettes;
|
||||
ListBase paintcurves;
|
||||
ListBase wm; /* Singleton (exception). */
|
||||
ListBase wm; /* Singleton (exception). */
|
||||
ListBase gpencils; /* Legacy Grease Pencil. */
|
||||
ListBase movieclips;
|
||||
ListBase masks;
|
||||
|
|
|
@ -43,7 +43,7 @@ float3 poly_center_calc(Span<float3> vert_positions, Span<MLoop> poly_loops);
|
|||
/** Calculate the surface area of the polygon described by the indexed vertices. */
|
||||
float poly_area_calc(Span<float3> vert_positions, Span<MLoop> poly_loops);
|
||||
|
||||
/** Calculate the angles at each of the polygons's corners. */
|
||||
/** Calculate the angles at each of the polygons corners. */
|
||||
void poly_angles_calc(Span<float3> vert_positions,
|
||||
Span<MLoop> poly_loops,
|
||||
MutableSpan<float> angles);
|
||||
|
|
|
@ -45,7 +45,7 @@ void BKE_mesh_remap_item_define_invalid(MeshPairRemap *map, int index);
|
|||
* https://blenderartists.org/t/619105
|
||||
*
|
||||
* We could also use similar topology mappings inside a same mesh
|
||||
* (cf. Campbell's 'select face islands from similar topology' wip work).
|
||||
* (cf. Campbell's 'select face islands from similar topology' WIP work).
|
||||
* Also, users will have to check, whether we can get rid of some modes here,
|
||||
* not sure all will be useful!
|
||||
*/
|
||||
|
|
|
@ -497,6 +497,7 @@ set(SRC
|
|||
nla_private.h
|
||||
particle_private.h
|
||||
tracking_private.h
|
||||
|
||||
intern/CCGSubSurf.h
|
||||
intern/CCGSubSurf_inline.h
|
||||
intern/CCGSubSurf_intern.h
|
||||
|
@ -508,6 +509,7 @@ set(SRC
|
|||
intern/multires_unsubdivide.h
|
||||
intern/ocean_intern.h
|
||||
intern/pbvh_intern.hh
|
||||
intern/pbvh_pixels_copy.hh
|
||||
intern/pbvh_uv_islands.hh
|
||||
intern/subdiv_converter.h
|
||||
intern/subdiv_inline.h
|
||||
|
|
|
@ -1141,11 +1141,12 @@ static void collection_gobject_hash_ensure_fix(Collection *collection)
|
|||
/**
|
||||
* Update the collections object hash, removing `ob_old`, inserting `cob->ob` as the new key.
|
||||
*
|
||||
* \note This function is called fron foreach_id callback, and a difference of Object pointers is
|
||||
* only expected in case ID remapping is happening. This code is the only are in Blender allowed to
|
||||
* (temporarily) leave the CollectionObject list in an inconsistent/invalid state (with NULL object
|
||||
* pointers, or duplicates of CollectionObjects). If such invalid cases are encountered, it will
|
||||
* tag the collection objects list as dirty.
|
||||
* \note This function is called from #IDTypeInfo::foreach_id callback,
|
||||
* and a difference of Object pointers is only expected in case ID remapping is happening.
|
||||
* This code is the only are in Blender allowed to (temporarily) leave the #CollectionObject list
|
||||
* in an inconsistent/invalid state (with NULL object pointers, or duplicates of
|
||||
* #CollectionObjects). If such invalid cases are encountered,
|
||||
* it will tag the collection objects list as dirty.
|
||||
*
|
||||
* \param ob_old: The existing key to `cob` in the hash, not removed when NULL.
|
||||
* \param cob: The `cob->ob` is to be used as the new key,
|
||||
|
@ -1162,7 +1163,7 @@ static void collection_gobject_hash_update_object(Collection *collection,
|
|||
if (ob_old) {
|
||||
CollectionObject *cob_old = BLI_ghash_popkey(collection->runtime.gobject_hash, ob_old, NULL);
|
||||
if (cob_old != cob) {
|
||||
/* Old object alredy removed from the ghash. */
|
||||
/* Old object already removed from the #GHash. */
|
||||
collection->runtime.tag |= COLLECTION_TAG_COLLECTION_OBJECT_DIRTY;
|
||||
}
|
||||
}
|
||||
|
@ -1173,28 +1174,28 @@ static void collection_gobject_hash_update_object(Collection *collection,
|
|||
*cob_p = cob;
|
||||
}
|
||||
else {
|
||||
/* Duplicate CollectionObject entries. */
|
||||
/* Duplicate #CollectionObject entries. */
|
||||
collection->runtime.tag |= COLLECTION_TAG_COLLECTION_OBJECT_DIRTY;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* CollectionObject with NULL objetc pointer. */
|
||||
/* #CollectionObject with NULL object pointer. */
|
||||
collection->runtime.tag |= COLLECTION_TAG_COLLECTION_OBJECT_DIRTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the integrity of the collection's CollectionObject list, and of its mapping.
|
||||
* Validate the integrity of the collection's #CollectionObject list, and of its mapping.
|
||||
*
|
||||
* Simple test is very fast, as it only checks that the 'dirty' tag for collection's objects is not
|
||||
* set.
|
||||
*
|
||||
* The extensive check is expensive. This should not be done from within loops over collections
|
||||
* items, or from low-level operations that can be assumed safe (like adding or removing an object
|
||||
* from a colleciton). It ensures that:
|
||||
* - There is a `gobject_hash` mapping.
|
||||
* - There is no NULL-object CollectionObject items.
|
||||
* - there is no duplicate CollectionObject items (two or more referencing the same Object).
|
||||
* from a collection). It ensures that:
|
||||
* - There is a `gobject_hash` mapping.
|
||||
* - There is no NULL-object #CollectionObject items.
|
||||
* - there is no duplicate #CollectionObject items (two or more referencing the same Object).
|
||||
*/
|
||||
static void collection_gobject_assert_internal_consistency(Collection *collection,
|
||||
const bool do_extensive_check)
|
||||
|
@ -1205,16 +1206,16 @@ static void collection_gobject_assert_internal_consistency(Collection *collectio
|
|||
}
|
||||
|
||||
if (collection->runtime.gobject_hash == NULL) {
|
||||
/* NOTE: If the ghash does not exist yet, it's creation will assert on errors, so in theory the
|
||||
* second loop below could be skipped. */
|
||||
/* NOTE: If the `ghash` does not exist yet, it's creation will assert on errors,
|
||||
* so in theory the second loop below could be skipped. */
|
||||
collection_gobject_hash_create(collection);
|
||||
}
|
||||
GHash *gobject_hash = collection->runtime.gobject_hash;
|
||||
UNUSED_VARS_NDEBUG(gobject_hash);
|
||||
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
|
||||
BLI_assert(cob->ob != NULL);
|
||||
/* If there are more than one CollectionObject for the same object, at most one of them will
|
||||
* pass this test. */
|
||||
/* If there are more than one #CollectionObject for the same object,
|
||||
* at most one of them will pass this test. */
|
||||
BLI_assert(BLI_ghash_lookup(gobject_hash, cob->ob) == cob);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -556,9 +556,12 @@ int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[],
|
|||
|
||||
/* ...................................... */
|
||||
|
||||
/* Get the first and last index to the bezt array that satisfies the given parameters.
|
||||
* \param selected_keys_only Only accept indices of bezt that are selected. Is a subset of
|
||||
* frame_range. \param frame_range Only consider keyframes in that frame interval. Can be NULL.
|
||||
/**
|
||||
* Get the first and last index to the bezt array that satisfies the given parameters.
|
||||
*
|
||||
* \param selected_keys_only: Only accept indices of bezt that are selected.
|
||||
* Is a subset of frame_range.
|
||||
* \param frame_range: Only consider keyframes in that frame interval. Can be NULL.
|
||||
*/
|
||||
static bool get_bounding_bezt_indices(const FCurve *fcu,
|
||||
const bool selected_keys_only,
|
||||
|
|
|
@ -422,7 +422,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
|
||||
/* All keys. */
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, false /* sel only */, false /* include handles */, NULL /* frame range */, &bounds);
|
||||
fcu, false /* select only */, false /* include handles */, NULL /* frame range */, &bounds);
|
||||
EXPECT_TRUE(success) << "A non-empty FCurve should have bounds.";
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[0].vec[1][0], bounds.xmin);
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[4].vec[1][0], bounds.xmax);
|
||||
|
@ -431,7 +431,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
|
||||
/* Only selected. */
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, true /* sel only */, false /* include handles */, NULL /* frame range */, &bounds);
|
||||
fcu, true /* select only */, false /* include handles */, NULL /* frame range */, &bounds);
|
||||
EXPECT_FALSE(success)
|
||||
<< "Using selected keyframes only should not find bounds if nothing is selected.";
|
||||
|
||||
|
@ -439,7 +439,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
fcu->bezt[3].f2 |= SELECT;
|
||||
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, true /* sel only */, false /* include handles */, NULL /* frame range */, &bounds);
|
||||
fcu, true /* select only */, false /* include handles */, NULL /* frame range */, &bounds);
|
||||
EXPECT_TRUE(success) << "Selected keys should have been found.";
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][0], bounds.xmin);
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[1][0], bounds.xmax);
|
||||
|
@ -448,7 +448,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
|
||||
/* Including handles. */
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, false /* sel only */, true /* include handles */, NULL /* frame range */, &bounds);
|
||||
fcu, false /* select only */, true /* include handles */, NULL /* frame range */, &bounds);
|
||||
EXPECT_TRUE(success) << "A non-empty FCurve should have bounds including handles.";
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[0].vec[0][0], bounds.xmin);
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[4].vec[2][0], bounds.xmax);
|
||||
|
@ -461,13 +461,13 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
range[0] = 25;
|
||||
range[1] = 30;
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds);
|
||||
fcu, false /* select only */, false /* include handles */, range /* frame range */, &bounds);
|
||||
EXPECT_FALSE(success) << "A frame range outside the range of keyframes should not find bounds.";
|
||||
|
||||
range[0] = 0;
|
||||
range[1] = 18.2f;
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds);
|
||||
fcu, false /* select only */, false /* include handles */, range /* frame range */, &bounds);
|
||||
EXPECT_TRUE(success) << "A frame range within the range of keyframes should find bounds.";
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[0].vec[1][0], bounds.xmin);
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[1][0], bounds.xmax);
|
||||
|
@ -476,7 +476,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
|
||||
/* Range and handles. */
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, false /* sel only */, true /* include handles */, range /* frame range */, &bounds);
|
||||
fcu, false /* select only */, true /* include handles */, range /* frame range */, &bounds);
|
||||
EXPECT_TRUE(success)
|
||||
<< "A frame range within the range of keyframes should find bounds with handles.";
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[0].vec[0][0], bounds.xmin);
|
||||
|
@ -488,7 +488,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
range[0] = 8.0f;
|
||||
range[1] = 18.2f;
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, true /* sel only */, true /* include handles */, range /* frame range */, &bounds);
|
||||
fcu, true /* select only */, true /* include handles */, range /* frame range */, &bounds);
|
||||
EXPECT_TRUE(success)
|
||||
<< "A frame range within the range of keyframes should find bounds of selected keyframes.";
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[0][0], bounds.xmin);
|
||||
|
@ -502,7 +502,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
fcurve_store_samples(fcu, NULL, sample_start, sample_end, fcurve_samplingcb_evalcurve);
|
||||
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, false /* sel only */, false /* include handles */, NULL /* frame range */, &bounds);
|
||||
fcu, false /* select only */, false /* include handles */, NULL /* frame range */, &bounds);
|
||||
EXPECT_TRUE(success) << "FCurve samples should have a range.";
|
||||
|
||||
EXPECT_FLOAT_EQ(sample_start, bounds.xmin);
|
||||
|
@ -513,7 +513,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
range[0] = 8.0f;
|
||||
range[1] = 20.0f;
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds);
|
||||
fcu, false /* select only */, false /* include handles */, range /* frame range */, &bounds);
|
||||
EXPECT_TRUE(success) << "FCurve samples should have a range.";
|
||||
|
||||
EXPECT_FLOAT_EQ(range[0], bounds.xmin);
|
||||
|
@ -524,7 +524,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
range[0] = 20.1f;
|
||||
range[1] = 30.0f;
|
||||
success = BKE_fcurve_calc_bounds(
|
||||
fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds);
|
||||
fcu, false /* select only */, false /* include handles */, range /* frame range */, &bounds);
|
||||
EXPECT_FALSE(success)
|
||||
<< "A frame range outside the range of keyframe samples should not have bounds.";
|
||||
|
||||
|
|
|
@ -368,7 +368,7 @@ static void gpencil_convert_spline(Main *bmain,
|
|||
if ((nu->flagu & CU_NURB_CYCLIC) == 0) {
|
||||
segments--;
|
||||
}
|
||||
/* Get all interpolated curve points of Beziert */
|
||||
/* Get all interpolated curve points of Bezier. */
|
||||
for (int s = 0; s < segments; s++) {
|
||||
int inext = (s + 1) % nu->pntsu;
|
||||
BezTriple *prevbezt = &nu->bezt[s];
|
||||
|
@ -915,7 +915,7 @@ static float *gpencil_stroke_points_from_editcurve_adaptive_resolu(
|
|||
bool is_cyclic,
|
||||
int *r_points_len)
|
||||
{
|
||||
/* One stride contains: x, y, z, pressure, strength, Vr, Vg, Vb, Vmix_factor */
|
||||
/* One stride contains: `x, y, z, pressure, strength, Vr, Vg, Vb, Vmix_factor`. */
|
||||
const uint stride = sizeof(float[9]);
|
||||
const uint cpt_last = curve_point_array_len - 1;
|
||||
const uint num_segments = (is_cyclic) ? curve_point_array_len : curve_point_array_len - 1;
|
||||
|
|
|
@ -191,7 +191,7 @@ void BKE_gpencil_blend_read_data(BlendDataReader *reader, bGPdata *gpd)
|
|||
BLO_read_data_address(reader, &gpd->adt);
|
||||
BKE_animdata_blend_read_data(reader, gpd->adt);
|
||||
|
||||
/* Ensure full objectmode for linked grease pencil. */
|
||||
/* Ensure full object-mode for linked grease pencil. */
|
||||
if (ID_IS_LINKED(gpd)) {
|
||||
gpd->flag &= ~GP_DATA_STROKE_PAINTMODE;
|
||||
gpd->flag &= ~GP_DATA_STROKE_EDITMODE;
|
||||
|
@ -1364,9 +1364,9 @@ bGPDframe *BKE_gpencil_layer_frame_get(bGPDlayer *gpl, int cframe, eGP_GetFrame_
|
|||
gpl->actframe = gpl->frames.first;
|
||||
}
|
||||
else {
|
||||
/* unresolved errogenous situation! */
|
||||
/* Unresolved erogenous situation! */
|
||||
CLOG_STR_ERROR(&LOG, "cannot find appropriate gp-frame");
|
||||
/* gpl->actframe should still be NULL */
|
||||
/* `gpl->actframe` should still be NULL. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2324,7 +2324,7 @@ bool BKE_gpencil_from_image(
|
|||
pt->strength = 1.0f - color[3];
|
||||
}
|
||||
|
||||
/* Selet Alpha points. */
|
||||
/* Select Alpha points. */
|
||||
if (pt->strength < 0.03f) {
|
||||
gps->flag |= GP_STROKE_SELECT;
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
|
|
|
@ -309,7 +309,7 @@ template<typename T> struct AngleCartesianBase {
|
|||
math::sqrt((T(1) - a.cos_) / T(2))};
|
||||
/* Recover sign only for sine. Cosine of half angle is given to be positive or 0 since the
|
||||
* angle stored in #AngleCartesianBase is in the range [-pi..pi]. */
|
||||
/* TODO(fclem): Could use copysign here. */
|
||||
/* TODO(fclem): Could use `copysign` here. */
|
||||
if (a.sin_ < T(0)) {
|
||||
result.sin_ = -result.sin_;
|
||||
}
|
||||
|
|
|
@ -1307,7 +1307,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
|
|||
/* Cannot call stuff now (pointers!), done in #setup_app_data. */
|
||||
ob->id.recalc |= ID_RECALC_ALL;
|
||||
|
||||
/* new generic xray option */
|
||||
/* New generic X-ray option. */
|
||||
arm = blo_do_versions_newlibadr(fd, lib, ob->data);
|
||||
enum { ARM_DRAWXRAY = (1 << 1) };
|
||||
if (arm->flag & ARM_DRAWXRAY) {
|
||||
|
|
|
@ -378,6 +378,8 @@ typedef struct BMesh {
|
|||
* This allows save invalidation of a #BMesh when it's freed,
|
||||
* so the Python object will report it as having been removed,
|
||||
* instead of crashing on invalid memory access.
|
||||
*
|
||||
* Doesn't hold a #PyObject reference, cleared when the last object is de-referenced.
|
||||
*/
|
||||
void *py_handle;
|
||||
} BMesh;
|
||||
|
|
|
@ -1169,9 +1169,9 @@ static void bm_face_loop_table_build(BMesh &bm,
|
|||
pin_offsets[i] = ldata.layers[pin_layers[i]].offset;
|
||||
}
|
||||
|
||||
Array<bool> need_vert_sel(vert_sel_layers.size());
|
||||
Array<bool> need_edge_sel(edge_sel_layers.size());
|
||||
Array<bool> need_pin(pin_layers.size());
|
||||
Array<bool> need_vert_sel(vert_sel_layers.size(), false);
|
||||
Array<bool> need_edge_sel(edge_sel_layers.size(), false);
|
||||
Array<bool> need_pin(pin_layers.size(), false);
|
||||
char hflag = 0;
|
||||
BMIter iter;
|
||||
int face_i = 0;
|
||||
|
|
|
@ -8,7 +8,7 @@ void node_composite_luminance_matte(vec4 color,
|
|||
out float matte)
|
||||
{
|
||||
float luminance = get_luminance(color.rgb, luminance_coefficients);
|
||||
float alpha = clamp(0.0, 1.0, (luminance - low) / (high - low));
|
||||
float alpha = clamp((luminance - low) / (high - low), 0.0, 1.0);
|
||||
matte = min(alpha, color.a);
|
||||
result = color * matte;
|
||||
}
|
||||
|
|
|
@ -1243,7 +1243,7 @@ static void sculpt_draw_cb(DRWSculptCallbackData *scd,
|
|||
DRWShadingGroup *shgrp = scd->shading_groups[index];
|
||||
if (geom != nullptr && shgrp != nullptr) {
|
||||
if (SCULPT_DEBUG_BUFFERS) {
|
||||
/* Color each buffers in different colors. Only work in solid/Xray mode. */
|
||||
/* Color each buffers in different colors. Only work in solid/X-ray mode. */
|
||||
shgrp = DRW_shgroup_create_sub(shgrp);
|
||||
DRW_shgroup_uniform_vec3(
|
||||
shgrp, "materialDiffuseColor", SCULPT_DEBUG_COLOR(scd->debug_node_nr++), 1);
|
||||
|
|
|
@ -1715,6 +1715,10 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
|
|||
*/
|
||||
|
||||
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
|
||||
/* Active frame is copied later, so don't need duplicate the stroke here. */
|
||||
if (gpl->actframe == gpf) {
|
||||
continue;
|
||||
}
|
||||
if (gpf->flag & GP_FRAME_SELECT) {
|
||||
if (gpf) {
|
||||
/* Create new stroke */
|
||||
|
|
|
@ -1411,7 +1411,8 @@ static float gpencil_sculpt_rotation_eval_get(tGP_BrushEditData *gso,
|
|||
|
||||
const GP_SpaceConversion *gsc = &gso->gsc;
|
||||
bGPDstroke *gps_orig = (gps_eval->runtime.gps_orig) ? gps_eval->runtime.gps_orig : gps_eval;
|
||||
bGPDspoint *pt_orig = &gps_orig->points[pt_eval->runtime.idx_orig];
|
||||
bGPDspoint *pt_orig = (pt_eval->runtime.pt_orig) ? &gps_orig->points[pt_eval->runtime.idx_orig] :
|
||||
pt_eval;
|
||||
bGPDspoint *pt_prev_eval = NULL;
|
||||
bGPDspoint *pt_orig_prev = NULL;
|
||||
if (idx_eval != 0) {
|
||||
|
|
|
@ -213,10 +213,10 @@ void paintface_reveal(bContext *C, Object *ob, const bool select)
|
|||
}
|
||||
|
||||
/**
|
||||
* Join all edges of each poly in the AtomicDisjointSet. This can be used to find out which polys
|
||||
* Join all edges of each poly in the #AtomicDisjointSet. This can be used to find out which polys
|
||||
* are connected to each other.
|
||||
* \param islands Is expected to be of length mesh->totedge.
|
||||
* \param skip_seams Polys separated by a seam will be treated as not connected.
|
||||
* \param islands: Is expected to be of length `mesh->totedge`.
|
||||
* \param skip_seams: Polys separated by a seam will be treated as not connected.
|
||||
*/
|
||||
static void build_poly_connections(blender::AtomicDisjointSet &islands,
|
||||
Mesh &mesh,
|
||||
|
|
|
@ -174,7 +174,7 @@ static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], L
|
|||
ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
|
||||
if (U.animation_flag &
|
||||
USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) { /* FIXME: this should really be check for by the
|
||||
filtering code... */
|
||||
* filtering code. */
|
||||
filter |= ANIMFILTER_SEL;
|
||||
}
|
||||
mapping_flag |= ANIM_get_normalization_flags(ac);
|
||||
|
|
|
@ -675,7 +675,7 @@ int view3d_opengl_select_ex(ViewContext *vc,
|
|||
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
|
||||
}
|
||||
|
||||
/* If in xray mode, we select the wires in priority. */
|
||||
/* If in X-ray mode, we select the wires in priority. */
|
||||
if (XRAY_ACTIVE(v3d) && use_nearest) {
|
||||
/* We need to call "GPU_select_*" API's inside DRW_draw_select_loop
|
||||
* because the OpenGL context created & destroyed inside this function. */
|
||||
|
|
|
@ -3200,7 +3200,7 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
|
|||
Depsgraph *depsgraph,
|
||||
const ARegion *region,
|
||||
const View3D *v3d,
|
||||
const eSnapMode snap_to_flag,
|
||||
eSnapMode snap_to_flag,
|
||||
const SnapObjectParams *params,
|
||||
const float init_co[3],
|
||||
const float mval[2],
|
||||
|
@ -3235,7 +3235,16 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
|
|||
|
||||
const RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
|
||||
|
||||
bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(v3d);
|
||||
if (snap_to_flag & (SCE_SNAP_MODE_FACE_RAYCAST | SCE_SNAP_MODE_FACE_NEAREST)) {
|
||||
if (params->use_occlusion_test && XRAY_ENABLED(v3d)) {
|
||||
/* Remove Snap to Face with Occlusion Test as they are not visible in wireframe mode. */
|
||||
snap_to_flag &= ~(SCE_SNAP_MODE_FACE_RAYCAST | SCE_SNAP_MODE_FACE_NEAREST);
|
||||
}
|
||||
else if (prev_co == nullptr || init_co == nullptr) {
|
||||
/* No location to work with #SCE_SNAP_MODE_FACE_NEAREST. */
|
||||
snap_to_flag &= ~SCE_SNAP_MODE_FACE_NEAREST;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: if both face ray-cast and face nearest are enabled, first find result of nearest, then
|
||||
* override with ray-cast. */
|
||||
|
@ -3261,7 +3270,7 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
|
|||
}
|
||||
}
|
||||
|
||||
if (snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST || use_occlusion_test) {
|
||||
if (snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST) {
|
||||
float ray_start[3], ray_normal[3];
|
||||
if (!ED_view3d_win_to_ray_clipped_ex(
|
||||
depsgraph, region, v3d, mval, nullptr, ray_normal, ray_start, true)) {
|
||||
|
|
|
@ -90,6 +90,12 @@ void VKBuffer::read(void *data) const
|
|||
memcpy(data, mapped_memory_, size_in_bytes_);
|
||||
}
|
||||
|
||||
void *VKBuffer::mapped_memory_get() const
|
||||
{
|
||||
BLI_assert_msg(is_mapped(), "Cannot access a non-mapped buffer.");
|
||||
return mapped_memory_;
|
||||
}
|
||||
|
||||
bool VKBuffer::is_mapped() const
|
||||
{
|
||||
return mapped_memory_ != nullptr;
|
||||
|
|
|
@ -49,6 +49,13 @@ class VKBuffer {
|
|||
return vk_buffer_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reference to the mapped memory.
|
||||
*
|
||||
* Can only be called when the buffer is (still) mapped.
|
||||
*/
|
||||
void *mapped_memory_get() const;
|
||||
|
||||
private:
|
||||
/** Check if this buffer is mapped. */
|
||||
bool is_mapped() const;
|
||||
|
|
|
@ -11,20 +11,28 @@ namespace blender::gpu {
|
|||
|
||||
VKPixelBuffer::VKPixelBuffer(int64_t size) : PixelBuffer(size)
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
buffer_.create(context,
|
||||
size,
|
||||
GPU_USAGE_STATIC,
|
||||
static_cast<VkBufferUsageFlagBits>(VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT));
|
||||
}
|
||||
|
||||
void *VKPixelBuffer::map()
|
||||
{
|
||||
return nullptr;
|
||||
/* Vulkan buffers are always mapped between allocation and freeing. */
|
||||
return buffer_.mapped_memory_get();
|
||||
}
|
||||
|
||||
void VKPixelBuffer::unmap()
|
||||
{
|
||||
/* Vulkan buffers are always mapped between allocation and freeing. */
|
||||
}
|
||||
|
||||
int64_t VKPixelBuffer::get_native_handle()
|
||||
{
|
||||
return -1;
|
||||
return int64_t(buffer_.vk_handle());
|
||||
}
|
||||
|
||||
uint VKPixelBuffer::get_size()
|
||||
|
|
|
@ -9,9 +9,13 @@
|
|||
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
#include "vk_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class VKPixelBuffer : public PixelBuffer {
|
||||
VKBuffer buffer_;
|
||||
|
||||
public:
|
||||
VKPixelBuffer(int64_t size);
|
||||
void *map() override;
|
||||
|
|
|
@ -18,6 +18,7 @@ set(INC_SYS
|
|||
${JPEG_INCLUDE_DIR}
|
||||
${PNG_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
${OPENIMAGEIO_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(SRC
|
||||
|
@ -29,6 +30,7 @@ set(SRC
|
|||
intern/divers.c
|
||||
intern/filetype.c
|
||||
intern/filter.c
|
||||
intern/format_psd.cc
|
||||
intern/imageprocess.c
|
||||
intern/indexer.c
|
||||
intern/iris.c
|
||||
|
|
|
@ -678,7 +678,7 @@ void IMB_sampleImageAtLocation(
|
|||
* \attention defined in readimage.c
|
||||
*/
|
||||
struct ImBuf *IMB_loadifffile(
|
||||
int file, const char *filepath, int flags, char colorspace[IM_MAX_SPACE], const char *descr);
|
||||
int file, int flags, char colorspace[IM_MAX_SPACE], const char *descr);
|
||||
|
||||
/**
|
||||
* \attention defined in scaling.c
|
||||
|
|
|
@ -318,9 +318,6 @@ extern const char *imb_ext_image[];
|
|||
extern const char *imb_ext_movie[];
|
||||
extern const char *imb_ext_audio[];
|
||||
|
||||
/** Image formats that can only be loaded via filepath. */
|
||||
extern const char *imb_ext_image_filepath_only[];
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Imbuf Color Management Flag
|
||||
*
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
|
||||
#include "IMB_imbuf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generic File Type
|
||||
* \{ */
|
||||
|
@ -256,3 +260,20 @@ struct ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath,
|
|||
bool imb_savewebp(struct ImBuf *ibuf, const char *name, int flags);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Format: PSD (#IMB_FTYPE_PSD)
|
||||
* \{ */
|
||||
|
||||
bool imb_is_a_psd(const unsigned char *buf, size_t size);
|
||||
|
||||
struct ImBuf *imb_load_psd(const uchar *mem,
|
||||
size_t size,
|
||||
int flags,
|
||||
char colorspace[IM_MAX_SPACE]);
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -184,9 +184,9 @@ const ImFileType IMB_FILE_TYPES[] = {
|
|||
{
|
||||
.init = NULL,
|
||||
.exit = NULL,
|
||||
.is_a = imb_is_a_photoshop,
|
||||
.load = NULL,
|
||||
.load_filepath = imb_load_photoshop,
|
||||
.is_a = imb_is_a_psd,
|
||||
.load = imb_load_psd,
|
||||
.load_filepath = NULL,
|
||||
.load_filepath_thumbnail = NULL,
|
||||
.save = NULL,
|
||||
.flag = IM_FTYPE_FLOAT,
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "oiio/openimageio_support.hh"
|
||||
|
||||
#include "IMB_filetype.h"
|
||||
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
OIIO_NAMESPACE_USING
|
||||
using namespace blender::imbuf;
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool imb_is_a_psd(const uchar *mem, size_t size)
|
||||
{
|
||||
return imb_oiio_check(mem, size, "psd");
|
||||
}
|
||||
|
||||
ImBuf *imb_load_psd(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
|
||||
{
|
||||
ImageSpec config, spec;
|
||||
config.attribute("oiio:UnassociatedAlpha", 1);
|
||||
|
||||
ReadContext ctx{mem, size, "psd", IMB_FTYPE_PSD, flags};
|
||||
|
||||
/* PSD should obey color space information embedded in the file. */
|
||||
ctx.use_embedded_colorspace = true;
|
||||
|
||||
return imb_oiio_read(ctx, config, colorspace, spec);
|
||||
}
|
||||
}
|
|
@ -18,8 +18,10 @@ set(INC_SYS
|
|||
|
||||
set(SRC
|
||||
openimageio_api.h
|
||||
openimageio_support.hh
|
||||
|
||||
openimageio_api.cpp
|
||||
openimageio_support.cc
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
|
|
@ -5,278 +5,16 @@
|
|||
* \ingroup openimageio
|
||||
*/
|
||||
|
||||
#include <set>
|
||||
|
||||
#if defined(WIN32)
|
||||
# include "utfconv.h"
|
||||
# define _USE_MATH_DEFINES
|
||||
#endif
|
||||
|
||||
/* NOTE: Keep first, #BLI_path_util conflicts with OIIO's format. */
|
||||
#include "openimageio_api.h"
|
||||
#include <OpenImageIO/imageio.h>
|
||||
#include <memory>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
|
||||
#include "IMB_allocimbuf.h"
|
||||
#include "IMB_colormanagement.h"
|
||||
#include "IMB_colormanagement_intern.h"
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
OIIO_NAMESPACE_USING
|
||||
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
|
||||
using uchar = uchar;
|
||||
|
||||
template<class T, class Q>
|
||||
static void fill_all_channels(T *pixels, int width, int height, int components, Q alpha)
|
||||
{
|
||||
if (components == 2) {
|
||||
for (int i = width * height - 1; i >= 0; i--) {
|
||||
pixels[i * 4 + 3] = pixels[i * 2 + 1];
|
||||
pixels[i * 4 + 2] = pixels[i * 2 + 0];
|
||||
pixels[i * 4 + 1] = pixels[i * 2 + 0];
|
||||
pixels[i * 4 + 0] = pixels[i * 2 + 0];
|
||||
}
|
||||
}
|
||||
else if (components == 3) {
|
||||
for (int i = width * height - 1; i >= 0; i--) {
|
||||
pixels[i * 4 + 3] = alpha;
|
||||
pixels[i * 4 + 2] = pixels[i * 3 + 2];
|
||||
pixels[i * 4 + 1] = pixels[i * 3 + 1];
|
||||
pixels[i * 4 + 0] = pixels[i * 3 + 0];
|
||||
}
|
||||
}
|
||||
else if (components == 1) {
|
||||
for (int i = width * height - 1; i >= 0; i--) {
|
||||
pixels[i * 4 + 3] = alpha;
|
||||
pixels[i * 4 + 2] = pixels[i];
|
||||
pixels[i * 4 + 1] = pixels[i];
|
||||
pixels[i * 4 + 0] = pixels[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ImBuf *imb_oiio_load_image(
|
||||
ImageInput *in, int width, int height, int components, int flags, bool is_alpha)
|
||||
{
|
||||
ImBuf *ibuf;
|
||||
int scanlinesize = width * components * sizeof(uchar);
|
||||
|
||||
/* allocate the memory for the image */
|
||||
ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, flags | IB_rect);
|
||||
|
||||
try {
|
||||
if (!in->read_image(0,
|
||||
0,
|
||||
0,
|
||||
components,
|
||||
TypeDesc::UINT8,
|
||||
(uchar *)ibuf->rect + (height - 1) * scanlinesize,
|
||||
AutoStride,
|
||||
-scanlinesize,
|
||||
AutoStride)) {
|
||||
std::cerr << __func__ << ": ImageInput::read_image() failed:" << std::endl
|
||||
<< in->geterror() << std::endl;
|
||||
|
||||
if (ibuf) {
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &exc) {
|
||||
std::cerr << exc.what() << std::endl;
|
||||
if (ibuf) {
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* ImBuf always needs 4 channels */
|
||||
fill_all_channels((uchar *)ibuf->rect, width, height, components, 0xFF);
|
||||
|
||||
return ibuf;
|
||||
}
|
||||
|
||||
static ImBuf *imb_oiio_load_image_float(
|
||||
ImageInput *in, int width, int height, int components, int flags, bool is_alpha)
|
||||
{
|
||||
ImBuf *ibuf;
|
||||
int scanlinesize = width * components * sizeof(float);
|
||||
|
||||
/* allocate the memory for the image */
|
||||
ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, flags | IB_rectfloat);
|
||||
|
||||
try {
|
||||
if (!in->read_image(0,
|
||||
0,
|
||||
0,
|
||||
components,
|
||||
TypeDesc::FLOAT,
|
||||
(uchar *)ibuf->rect_float + (height - 1) * scanlinesize,
|
||||
AutoStride,
|
||||
-scanlinesize,
|
||||
AutoStride)) {
|
||||
std::cerr << __func__ << ": ImageInput::read_image() failed:" << std::endl
|
||||
<< in->geterror() << std::endl;
|
||||
|
||||
if (ibuf) {
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &exc) {
|
||||
std::cerr << exc.what() << std::endl;
|
||||
if (ibuf) {
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* ImBuf always needs 4 channels */
|
||||
fill_all_channels((float *)ibuf->rect_float, width, height, components, 1.0f);
|
||||
|
||||
/* NOTE: Photoshop 16 bit files never has alpha with it,
|
||||
* so no need to handle associated/unassociated alpha. */
|
||||
return ibuf;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool imb_is_a_photoshop(const uchar *mem, size_t size)
|
||||
{
|
||||
const uchar magic[4] = {'8', 'B', 'P', 'S'};
|
||||
if (size < sizeof(magic)) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(magic, mem, sizeof(magic)) == 0;
|
||||
}
|
||||
|
||||
int imb_save_photoshop(struct ImBuf *ibuf, const char * /*name*/, int flags)
|
||||
{
|
||||
if (flags & IB_mem) {
|
||||
std::cerr << __func__ << ": Photoshop PSD-save: Create PSD in memory"
|
||||
<< " currently not supported" << std::endl;
|
||||
imb_addencodedbufferImBuf(ibuf);
|
||||
ibuf->encodedsize = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ImBuf *imb_load_photoshop(const char *filename, int flags, char colorspace[IM_MAX_SPACE])
|
||||
{
|
||||
struct ImBuf *ibuf = nullptr;
|
||||
int width, height, components;
|
||||
bool is_float, is_alpha, is_half;
|
||||
int basesize;
|
||||
char file_colorspace[IM_MAX_SPACE];
|
||||
const bool is_colorspace_manually_set = (colorspace[0] != '\0');
|
||||
|
||||
/* load image from file through OIIO */
|
||||
if (IMB_ispic_type_matches(filename, IMB_FTYPE_PSD) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
|
||||
|
||||
unique_ptr<ImageInput> in(ImageInput::create(filename));
|
||||
if (!in) {
|
||||
std::cerr << __func__ << ": ImageInput::create() failed:" << std::endl
|
||||
<< OIIO_NAMESPACE::geterror() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ImageSpec spec, config;
|
||||
config.attribute("oiio:UnassociatedAlpha", int(1));
|
||||
|
||||
if (!in->open(filename, spec, config)) {
|
||||
std::cerr << __func__ << ": ImageInput::open() failed:" << std::endl
|
||||
<< in->geterror() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!is_colorspace_manually_set) {
|
||||
string ics = spec.get_string_attribute("oiio:ColorSpace");
|
||||
BLI_strncpy(file_colorspace, ics.c_str(), IM_MAX_SPACE);
|
||||
|
||||
/* Only use color-spaces exist. */
|
||||
if (colormanage_colorspace_get_named(file_colorspace)) {
|
||||
strcpy(colorspace, file_colorspace);
|
||||
}
|
||||
else {
|
||||
std::cerr << __func__ << ": The embed colorspace (\"" << file_colorspace
|
||||
<< "\") not supported in existent OCIO configuration file. Fallback "
|
||||
<< "to system default colorspace (\"" << colorspace << "\")." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
width = spec.width;
|
||||
height = spec.height;
|
||||
components = spec.nchannels;
|
||||
is_alpha = spec.alpha_channel != -1;
|
||||
basesize = spec.format.basesize();
|
||||
is_float = basesize > 1;
|
||||
is_half = spec.format == TypeDesc::HALF;
|
||||
|
||||
/* we only handle certain number of components */
|
||||
if (!(components >= 1 && components <= 4)) {
|
||||
if (in) {
|
||||
in->close();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (is_float) {
|
||||
ibuf = imb_oiio_load_image_float(in.get(), width, height, components, flags, is_alpha);
|
||||
}
|
||||
else {
|
||||
ibuf = imb_oiio_load_image(in.get(), width, height, components, flags, is_alpha);
|
||||
}
|
||||
|
||||
if (in) {
|
||||
in->close();
|
||||
}
|
||||
|
||||
if (!ibuf) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* ImBuf always needs 4 channels */
|
||||
ibuf->ftype = IMB_FTYPE_PSD;
|
||||
ibuf->channels = 4;
|
||||
ibuf->planes = (3 + (is_alpha ? 1 : 0)) * 4 << basesize;
|
||||
ibuf->flags |= (is_float && is_half) ? IB_halffloat : 0;
|
||||
|
||||
try {
|
||||
return ibuf;
|
||||
}
|
||||
catch (const std::exception &exc) {
|
||||
std::cerr << exc.what() << std::endl;
|
||||
if (ibuf) {
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int OIIO_getVersionHex(void)
|
||||
{
|
||||
return openimageio_version();
|
||||
}
|
||||
|
||||
} /* export "C" */
|
||||
} /* extern "C" */
|
||||
|
|
|
@ -7,20 +7,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ImBuf;
|
||||
|
||||
bool imb_is_a_photoshop(const unsigned char *mem, size_t size);
|
||||
|
||||
int imb_save_photoshop(struct ImBuf *ibuf, const char *name, int flags);
|
||||
|
||||
struct ImBuf *imb_load_photoshop(const char *name, int flags, char *colorspace);
|
||||
|
||||
int OIIO_getVersionHex(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -0,0 +1,398 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "openimageio_support.hh"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
|
||||
#include "BKE_idprop.h"
|
||||
#include "DNA_ID.h" /* ID property definitions. */
|
||||
|
||||
#include "IMB_allocimbuf.h"
|
||||
#include "IMB_colormanagement.h"
|
||||
#include "IMB_metadata.h"
|
||||
|
||||
OIIO_NAMESPACE_USING
|
||||
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
|
||||
namespace blender::imbuf {
|
||||
|
||||
/* An OIIO IOProxy used during file packing to write into an in-memory #ImBuf buffer. */
|
||||
class ImBufMemWriter : public Filesystem::IOProxy {
|
||||
public:
|
||||
ImBufMemWriter(ImBuf *ibuf) : IOProxy("", Write), ibuf_(ibuf)
|
||||
{
|
||||
}
|
||||
|
||||
const char *proxytype() const override
|
||||
{
|
||||
return "ImBufMemWriter";
|
||||
}
|
||||
|
||||
size_t write(const void *buf, size_t size) override
|
||||
{
|
||||
size = pwrite(buf, size, m_pos);
|
||||
m_pos += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t pwrite(const void *buf, size_t size, int64_t offset) override
|
||||
{
|
||||
/* If buffer is too small increase it. */
|
||||
size_t end = offset + size;
|
||||
while (end > ibuf_->encodedbuffersize) {
|
||||
if (!imb_enlargeencodedbufferImBuf(ibuf_)) {
|
||||
/* Out of memory. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ibuf_->encodedbuffer + offset, buf, size);
|
||||
|
||||
if (end > ibuf_->encodedsize) {
|
||||
ibuf_->encodedsize = end;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t size() const override
|
||||
{
|
||||
return ibuf_->encodedsize;
|
||||
}
|
||||
|
||||
private:
|
||||
ImBuf *ibuf_;
|
||||
};
|
||||
|
||||
/* Utility to in-place expand an n-component pixel buffer into a 4-component buffer. */
|
||||
template<typename T>
|
||||
static void fill_all_channels(T *pixels, int width, int height, int components, T alpha)
|
||||
{
|
||||
const int64_t pixel_count = int64_t(width) * height;
|
||||
if (components == 3) {
|
||||
for (int64_t i = 0; i < pixel_count; i++) {
|
||||
pixels[i * 4 + 3] = alpha;
|
||||
}
|
||||
}
|
||||
else if (components == 1) {
|
||||
for (int64_t i = 0; i < pixel_count; i++) {
|
||||
pixels[i * 4 + 3] = alpha;
|
||||
pixels[i * 4 + 2] = pixels[i * 4 + 0];
|
||||
pixels[i * 4 + 1] = pixels[i * 4 + 0];
|
||||
}
|
||||
}
|
||||
else if (components == 2) {
|
||||
for (int64_t i = 0; i < pixel_count; i++) {
|
||||
pixels[i * 4 + 3] = pixels[i * 4 + 1];
|
||||
pixels[i * 4 + 2] = pixels[i * 4 + 0];
|
||||
pixels[i * 4 + 1] = pixels[i * 4 + 0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static ImBuf *load_pixels(
|
||||
ImageInput *in, int width, int height, int channels, int flags, bool use_all_planes)
|
||||
{
|
||||
/* Allocate the ImBuf for the image. */
|
||||
constexpr bool is_float = sizeof(T) > 1;
|
||||
const uint format_flag = is_float ? IB_rectfloat : IB_rect;
|
||||
const uint ibuf_flags = (flags & IB_test) ? 0 : format_flag;
|
||||
const int planes = use_all_planes ? 32 : 8 * channels;
|
||||
ImBuf *ibuf = IMB_allocImBuf(width, height, planes, ibuf_flags);
|
||||
if (!ibuf) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* No need to load actual pixel data during the test phase. */
|
||||
if (flags & IB_test) {
|
||||
return ibuf;
|
||||
}
|
||||
|
||||
/* Calculate an appropriate stride to read n-channels directly into
|
||||
* the ImBuf 4-channel layout. */
|
||||
const stride_t ibuf_xstride = sizeof(T) * 4;
|
||||
const stride_t ibuf_ystride = ibuf_xstride * width;
|
||||
const TypeDesc format = is_float ? TypeDesc::FLOAT : TypeDesc::UINT8;
|
||||
uchar *rect = is_float ? reinterpret_cast<uchar *>(ibuf->rect_float) :
|
||||
reinterpret_cast<uchar *>(ibuf->rect);
|
||||
void *ibuf_data = rect + ((stride_t(height) - 1) * ibuf_ystride);
|
||||
|
||||
bool ok = in->read_image(
|
||||
0, 0, 0, channels, format, ibuf_data, ibuf_xstride, -ibuf_ystride, AutoStride);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "ImageInput::read_image() failed: %s\n", in->geterror().c_str());
|
||||
|
||||
IMB_freeImBuf(ibuf);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* ImBuf always needs 4 channels */
|
||||
const T alpha_fill = is_float ? 1.0f : 0xFF;
|
||||
fill_all_channels<T>(reinterpret_cast<T *>(rect), width, height, channels, alpha_fill);
|
||||
|
||||
return ibuf;
|
||||
}
|
||||
|
||||
static void set_colorspace_name(char colorspace[IM_MAX_SPACE],
|
||||
const ReadContext &ctx,
|
||||
const ImageSpec &spec,
|
||||
bool is_float)
|
||||
{
|
||||
const bool is_colorspace_set = (colorspace[0] != '\0');
|
||||
if (is_colorspace_set) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Use a default role unless otherwise specified. */
|
||||
if (ctx.use_colorspace_role >= 0) {
|
||||
colorspace_set_default_role(colorspace, IM_MAX_SPACE, ctx.use_colorspace_role);
|
||||
}
|
||||
else if (is_float) {
|
||||
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
|
||||
}
|
||||
else {
|
||||
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
|
||||
}
|
||||
|
||||
/* Override if necessary. */
|
||||
if (ctx.use_embedded_colorspace) {
|
||||
string ics = spec.get_string_attribute("oiio:ColorSpace");
|
||||
char file_colorspace[IM_MAX_SPACE];
|
||||
BLI_strncpy(file_colorspace, ics.c_str(), IM_MAX_SPACE);
|
||||
|
||||
/* Only use color-spaces that exist. */
|
||||
if (colormanage_colorspace_get_named(file_colorspace)) {
|
||||
BLI_strncpy(colorspace, file_colorspace, IM_MAX_SPACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an #ImBuf filled in with pixel data and associated metadata using the provided ImageInput.
|
||||
*/
|
||||
static ImBuf *get_oiio_ibuf(ImageInput *in, const ReadContext &ctx, char colorspace[IM_MAX_SPACE])
|
||||
{
|
||||
const ImageSpec &spec = in->spec();
|
||||
const int width = spec.width;
|
||||
const int height = spec.height;
|
||||
const int channels = spec.nchannels;
|
||||
const bool has_alpha = spec.alpha_channel != -1;
|
||||
const bool is_float = spec.format.basesize() > 1;
|
||||
|
||||
if (channels < 1 || channels > 4) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bool use_all_planes = has_alpha || ctx.use_all_planes;
|
||||
|
||||
ImBuf *ibuf = nullptr;
|
||||
if (is_float) {
|
||||
ibuf = load_pixels<float>(in, width, height, channels, ctx.flags, use_all_planes);
|
||||
ibuf->channels = 4;
|
||||
}
|
||||
else {
|
||||
ibuf = load_pixels<uchar>(in, width, height, channels, ctx.flags, use_all_planes);
|
||||
}
|
||||
|
||||
/* Fill in common ibuf properties. */
|
||||
if (ibuf) {
|
||||
ibuf->ftype = ctx.file_type;
|
||||
ibuf->flags |= (spec.format == TypeDesc::HALF) ? IB_halffloat : 0;
|
||||
|
||||
set_colorspace_name(colorspace, ctx, spec, is_float);
|
||||
|
||||
float x_res = spec.get_float_attribute("XResolution", 0.0f);
|
||||
float y_res = spec.get_float_attribute("YResolution", 0.0f);
|
||||
if (x_res > 0.0f && y_res > 0.0f) {
|
||||
double scale = 1.0;
|
||||
auto unit = spec.get_string_attribute("ResolutionUnit", "");
|
||||
if (unit == "in" || unit == "inch") {
|
||||
scale = 100.0 / 2.54;
|
||||
}
|
||||
else if (unit == "cm") {
|
||||
scale = 100.0;
|
||||
}
|
||||
ibuf->ppm[0] = scale * x_res;
|
||||
ibuf->ppm[1] = scale * y_res;
|
||||
}
|
||||
|
||||
/* Transfer metadata to the ibuf if necessary. */
|
||||
if (ctx.flags & IB_metadata) {
|
||||
IMB_metadata_ensure(&ibuf->metadata);
|
||||
ibuf->flags |= (spec.extra_attribs.empty()) ? 0 : IB_metadata;
|
||||
|
||||
for (const auto &attrib : spec.extra_attribs) {
|
||||
IMB_metadata_set_field(ibuf->metadata, attrib.name().c_str(), attrib.get_string().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ibuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ImageInput for the precise `format` requested using the provided IOMemReader.
|
||||
* If successful, the ImageInput will be opened and ready for operations. Null will be returned if
|
||||
* the format was not found or if the open call fails.
|
||||
*/
|
||||
static unique_ptr<ImageInput> get_oiio_reader(const char *format,
|
||||
const ImageSpec &config,
|
||||
Filesystem::IOMemReader &mem_reader,
|
||||
ImageSpec &r_newspec)
|
||||
{
|
||||
/* Attempt to create a reader based on the passed in format. */
|
||||
unique_ptr<ImageInput> in = ImageInput::create(format);
|
||||
if (!in) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Open the reader using the ioproxy. */
|
||||
in->set_ioproxy(&mem_reader);
|
||||
bool ok = in->open("", r_newspec, config);
|
||||
if (!ok) {
|
||||
in.reset();
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
bool imb_oiio_check(const uchar *mem, size_t mem_size, const char *file_format)
|
||||
{
|
||||
ImageSpec config, spec;
|
||||
|
||||
/* This memory proxy must remain alive for the full duration of the read. */
|
||||
Filesystem::IOMemReader mem_reader(cspan<uchar>(mem, mem_size));
|
||||
unique_ptr<ImageInput> in = get_oiio_reader(file_format, config, mem_reader, spec);
|
||||
return in ? true : false;
|
||||
}
|
||||
|
||||
ImBuf *imb_oiio_read(const ReadContext &ctx,
|
||||
const ImageSpec &config,
|
||||
char colorspace[IM_MAX_SPACE],
|
||||
ImageSpec &r_newspec)
|
||||
{
|
||||
/* This memory proxy must remain alive for the full duration of the read. */
|
||||
Filesystem::IOMemReader mem_reader(cspan<uchar>(ctx.mem_start, ctx.mem_size));
|
||||
unique_ptr<ImageInput> in = get_oiio_reader(ctx.file_format, config, mem_reader, r_newspec);
|
||||
if (!in) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return get_oiio_ibuf(in.get(), ctx, colorspace);
|
||||
}
|
||||
|
||||
bool imb_oiio_write(const WriteContext &ctx, const char *filepath, const ImageSpec &file_spec)
|
||||
{
|
||||
unique_ptr<ImageOutput> out = ImageOutput::create(ctx.file_format);
|
||||
if (!out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto write_op = [&out, &ctx]() {
|
||||
return out->write_image(
|
||||
ctx.mem_format, ctx.mem_start, ctx.mem_xstride, -ctx.mem_ystride, AutoStride);
|
||||
};
|
||||
|
||||
bool ok = false;
|
||||
if (ctx.flags & IB_mem) {
|
||||
/* This memory proxy must remain alive for the full duration of the write. */
|
||||
ImBufMemWriter writer(ctx.ibuf);
|
||||
|
||||
imb_addencodedbufferImBuf(ctx.ibuf);
|
||||
out->set_ioproxy(&writer);
|
||||
out->open("", file_spec);
|
||||
ok = write_op();
|
||||
}
|
||||
else {
|
||||
out->open(filepath, file_spec);
|
||||
ok = write_op();
|
||||
}
|
||||
|
||||
out->close();
|
||||
return ok;
|
||||
}
|
||||
|
||||
WriteContext imb_create_write_context(const char *file_format,
|
||||
ImBuf *ibuf,
|
||||
int flags,
|
||||
bool prefer_float)
|
||||
{
|
||||
WriteContext ctx{};
|
||||
ctx.file_format = file_format;
|
||||
ctx.ibuf = ibuf;
|
||||
ctx.flags = flags;
|
||||
|
||||
const int width = ibuf->x;
|
||||
const int height = ibuf->y;
|
||||
const bool use_float = prefer_float && (ibuf->rect_float != nullptr);
|
||||
if (use_float) {
|
||||
const int mem_channels = ibuf->channels ? ibuf->channels : 4;
|
||||
ctx.mem_xstride = sizeof(float) * mem_channels;
|
||||
ctx.mem_ystride = width * ctx.mem_xstride;
|
||||
ctx.mem_format = TypeDesc::FLOAT;
|
||||
ctx.mem_start = reinterpret_cast<uchar *>(ibuf->rect_float);
|
||||
}
|
||||
else {
|
||||
const int mem_channels = 4;
|
||||
ctx.mem_xstride = sizeof(uchar) * mem_channels;
|
||||
ctx.mem_ystride = width * ctx.mem_xstride;
|
||||
ctx.mem_format = TypeDesc::UINT8;
|
||||
ctx.mem_start = reinterpret_cast<uchar *>(ibuf->rect);
|
||||
}
|
||||
|
||||
/* We always write using a negative y-stride so ensure we start at the end. */
|
||||
ctx.mem_start = ctx.mem_start + ((stride_t(height) - 1) * ctx.mem_ystride);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
ImageSpec imb_create_write_spec(const WriteContext &ctx, int file_channels, TypeDesc data_format)
|
||||
{
|
||||
const int width = ctx.ibuf->x;
|
||||
const int height = ctx.ibuf->y;
|
||||
ImageSpec file_spec(width, height, file_channels, data_format);
|
||||
|
||||
/* Populate the spec with all common attributes.
|
||||
*
|
||||
* Care must be taken with the metadata:
|
||||
* - It should be processed first, before the "Resolution" metadata below, to
|
||||
* ensure the proper values end up in the ImageSpec
|
||||
* - It needs to filter format-specific metadata that may no longer apply to
|
||||
* the current format being written (e.g. metadata for tiff being written to a png)
|
||||
*/
|
||||
|
||||
if (ctx.ibuf->metadata) {
|
||||
for (IDProperty *prop = static_cast<IDProperty *>(ctx.ibuf->metadata->data.group.first); prop;
|
||||
prop = prop->next) {
|
||||
if (prop->type == IDP_STRING) {
|
||||
/* If this property has a prefixed name (oiio:, tiff:, etc.) and it belongs to
|
||||
* oiio or a different format, then skip. */
|
||||
if (char *colon = strchr(prop->name, ':')) {
|
||||
std::string prefix(prop->name, colon);
|
||||
Strutil::to_lower(prefix);
|
||||
if (prefix == "oiio" ||
|
||||
(!STREQ(prefix.c_str(), ctx.file_format) && OIIO::is_imageio_format_name(prefix))) {
|
||||
/* Skip this attribute. */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
file_spec.attribute(prop->name, IDP_String(prop));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.ibuf->ppm[0] > 0.0 && ctx.ibuf->ppm[1] > 0.0) {
|
||||
/* More OIIO formats support inch than meter. */
|
||||
file_spec.attribute("ResolutionUnit", "in");
|
||||
file_spec.attribute("XResolution", float(ctx.ibuf->ppm[0] * 0.0254));
|
||||
file_spec.attribute("YResolution", float(ctx.ibuf->ppm[1] * 0.0254));
|
||||
}
|
||||
|
||||
return file_spec;
|
||||
}
|
||||
|
||||
} // namespace blender::imbuf
|
|
@ -0,0 +1,104 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <OpenImageIO/filesystem.h>
|
||||
#include <OpenImageIO/imageio.h>
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
namespace blender::imbuf {
|
||||
|
||||
/**
|
||||
* Parameters and settings used while reading image formats.
|
||||
*/
|
||||
struct ReadContext {
|
||||
const uchar *mem_start;
|
||||
const size_t mem_size;
|
||||
const char *file_format;
|
||||
const eImbFileType file_type;
|
||||
const int flags;
|
||||
|
||||
/* Override the automatic color-role choice with the value specified here. */
|
||||
int use_colorspace_role = -1;
|
||||
|
||||
/* Allocate and use all ImBuf image planes even if the image has fewer. */
|
||||
bool use_all_planes = false;
|
||||
|
||||
/* Use the colorspace provided in the image metadata when available. */
|
||||
bool use_embedded_colorspace = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters and settings used while writing image formats.
|
||||
*/
|
||||
struct WriteContext {
|
||||
const char *file_format;
|
||||
ImBuf *ibuf;
|
||||
|
||||
OIIO::stride_t mem_xstride;
|
||||
OIIO::stride_t mem_ystride;
|
||||
OIIO::TypeDesc mem_format;
|
||||
uchar *mem_start;
|
||||
|
||||
int flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check to see if we can load and open the given file format.
|
||||
*/
|
||||
bool imb_oiio_check(const uchar *mem, size_t mem_size, const char *file_format);
|
||||
|
||||
/**
|
||||
* The primary method for reading data into an #ImBuf.
|
||||
*
|
||||
* During the `IB_test` phase of loading, the `colorspace` parameter will be populated
|
||||
* with the appropriate colorspace name.
|
||||
*
|
||||
* Upon return, the `r_newspec` parameter will contain image format information
|
||||
* which can be inspected afterwards if necessary.
|
||||
*/
|
||||
ImBuf *imb_oiio_read(const ReadContext &ctx,
|
||||
const OIIO::ImageSpec &config,
|
||||
char colorspace[IM_MAX_SPACE],
|
||||
OIIO::ImageSpec &r_newspec);
|
||||
|
||||
/**
|
||||
* The primary method for writing data from an #ImBuf to either a physical or in-memory
|
||||
* destination.
|
||||
*
|
||||
* The `file_spec` parameter will typically come from #imb_create_write_spec.
|
||||
*/
|
||||
bool imb_oiio_write(const WriteContext &ctx,
|
||||
const char *filepath,
|
||||
const OIIO::ImageSpec &file_spec);
|
||||
|
||||
/**
|
||||
* Create a #WriteContext based on the provided #ImBuf and format information.
|
||||
*
|
||||
* If the provided #ImBuf contains both byte and float buffers, the `prefer_float`
|
||||
* flag controls which buffer to use. By default, if a float buffer exists it will
|
||||
* be used.
|
||||
*/
|
||||
WriteContext imb_create_write_context(const char *file_format,
|
||||
ImBuf *ibuf,
|
||||
int flags,
|
||||
bool prefer_float = true);
|
||||
|
||||
/**
|
||||
* Returns an ImageSpec filled in with all common attributes associated with the #ImBuf
|
||||
* provided as part of the #WriteContext.
|
||||
*
|
||||
* This includes optional metadata that has been attached to the #ImBuf and which should be
|
||||
* written to the new file as necessary.
|
||||
*/
|
||||
OIIO::ImageSpec imb_create_write_spec(const WriteContext &ctx,
|
||||
int file_channels,
|
||||
OIIO::TypeDesc data_format);
|
||||
|
||||
} // namespace blender::imbuf
|
|
@ -441,7 +441,7 @@ static void openexr_header_metadata(Header *header, struct ImBuf *ibuf)
|
|||
IDProperty *prop;
|
||||
|
||||
for (prop = (IDProperty *)ibuf->metadata->data.group.first; prop; prop = prop->next) {
|
||||
if (prop->type == IDP_STRING) {
|
||||
if (prop->type == IDP_STRING && !STREQ(prop->name, "compression")) {
|
||||
header->insert(prop->name, StringAttribute(IDP_String(prop)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,44 +114,7 @@ ImBuf *IMB_ibImageFromMemory(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static ImBuf *IMB_ibImageFromFile(const char *filepath,
|
||||
int flags,
|
||||
char colorspace[IM_MAX_SPACE],
|
||||
const char *descr)
|
||||
{
|
||||
ImBuf *ibuf;
|
||||
const ImFileType *type;
|
||||
char effective_colorspace[IM_MAX_SPACE] = "";
|
||||
|
||||
if (colorspace) {
|
||||
BLI_strncpy(effective_colorspace, colorspace, sizeof(effective_colorspace));
|
||||
}
|
||||
|
||||
for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
|
||||
if (type->load_filepath) {
|
||||
ibuf = type->load_filepath(filepath, flags, effective_colorspace);
|
||||
if (ibuf) {
|
||||
imb_handle_alpha(ibuf, flags, colorspace, effective_colorspace);
|
||||
return ibuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & IB_test) == 0) {
|
||||
fprintf(stderr, "%s: unknown fileformat (%s)\n", __func__, descr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool imb_is_filepath_format(const char *filepath)
|
||||
{
|
||||
/* return true if this is one of the formats that can't be loaded from memory */
|
||||
return BLI_path_extension_check_array(filepath, imb_ext_image_filepath_only);
|
||||
}
|
||||
|
||||
ImBuf *IMB_loadifffile(
|
||||
int file, const char *filepath, int flags, char colorspace[IM_MAX_SPACE], const char *descr)
|
||||
ImBuf *IMB_loadifffile(int file, int flags, char colorspace[IM_MAX_SPACE], const char *descr)
|
||||
{
|
||||
ImBuf *ibuf;
|
||||
uchar *mem;
|
||||
|
@ -161,10 +124,6 @@ ImBuf *IMB_loadifffile(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (imb_is_filepath_format(filepath)) {
|
||||
return IMB_ibImageFromFile(filepath, flags, colorspace, descr);
|
||||
}
|
||||
|
||||
size = BLI_file_descriptor_size(file);
|
||||
|
||||
imb_mmap_lock();
|
||||
|
@ -198,7 +157,7 @@ ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_S
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ibuf = IMB_loadifffile(file, filepath, flags, colorspace, filepath);
|
||||
ibuf = IMB_loadifffile(file, flags, colorspace, filepath);
|
||||
|
||||
if (ibuf) {
|
||||
BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name));
|
||||
|
@ -277,7 +236,7 @@ ImBuf *IMB_testiffname(const char *filepath, int flags)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ibuf = IMB_loadifffile(file, filepath, flags | IB_test | IB_multilayer, colorspace, filepath);
|
||||
ibuf = IMB_loadifffile(file, flags | IB_test | IB_multilayer, colorspace, filepath);
|
||||
|
||||
if (ibuf) {
|
||||
BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name));
|
||||
|
|
|
@ -67,13 +67,6 @@ const char *imb_ext_image[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
const char *imb_ext_image_filepath_only[] = {
|
||||
".psd",
|
||||
".pdd",
|
||||
".psb",
|
||||
NULL,
|
||||
};
|
||||
|
||||
const char *imb_ext_movie[] = {
|
||||
".avi", ".flc", ".mov", ".movie", ".mp4", ".m4v", ".m2v", ".m2t", ".m2ts", ".mts",
|
||||
".ts", ".mv", ".avs", ".wmv", ".ogv", ".ogg", ".r3d", ".dv", ".mpeg", ".mpg",
|
||||
|
|
|
@ -145,10 +145,10 @@ void build_material_map(const Main *bmain, std::map<std::string, Material *> *r_
|
|||
* Returns an existing Blender material that corresponds to the USD material with the given path.
|
||||
* Returns null if no such material exists.
|
||||
*
|
||||
* \param mat_map Map a material name to a Blender material. Note that the name key
|
||||
* \param mat_map: Map a material name to a Blender material. Note that the name key
|
||||
* might be the Blender material name modified to be a valid USD identifier,
|
||||
* to match the material names in the imported USD.
|
||||
* \param usd_path_to_mat_name Map a USD material path to the imported Blender material name.
|
||||
* \param usd_path_to_mat_name: Map a USD material path to the imported Blender material name.
|
||||
*
|
||||
* The usd_path_to_mat_name is needed to determine the name of the Blender
|
||||
* material imported from a USD path in the case when a unique name was generated
|
||||
|
|
|
@ -505,7 +505,7 @@ typedef struct bGPDlayer {
|
|||
struct Object *parent;
|
||||
/** Inverse matrix (only used if parented). */
|
||||
float inverse[4][4];
|
||||
/** String describing subobject info, MAX_ID_NAME-2. */
|
||||
/** String describing sub-object info, `MAX_ID_NAME - 2`. */
|
||||
char parsubstr[64];
|
||||
short partype;
|
||||
|
||||
|
@ -574,7 +574,7 @@ typedef enum eGPDlayer_Flag {
|
|||
GP_LAYER_SELECT = (1 << 5),
|
||||
/* current frame for layer can't be changed */
|
||||
GP_LAYER_FRAMELOCK = (1 << 6),
|
||||
/* don't render xray (which is default) */
|
||||
/* Don't render X-ray (which is default). */
|
||||
GP_LAYER_NO_XRAY = (1 << 7),
|
||||
/* "volumetric" strokes */
|
||||
GP_LAYER_VOLUMETRIC = (1 << 10),
|
||||
|
@ -852,7 +852,7 @@ typedef enum eGP_OnionModes {
|
|||
GP_ONION_MODE_SELECTED = 2,
|
||||
} eGP_OnionModes;
|
||||
|
||||
/* xray modes (Depth Ordering) */
|
||||
/* X-ray modes (Depth Ordering). */
|
||||
typedef enum eGP_DepthOrdering {
|
||||
GP_XRAY_FRONT = 0,
|
||||
GP_XRAY_3DSPACE = 1,
|
||||
|
|
|
@ -2296,10 +2296,6 @@ typedef enum eSnapMode {
|
|||
SCE_SNAP_MODE_EDGE_PERPENDICULAR = (1 << 5),
|
||||
SCE_SNAP_MODE_FACE_NEAREST = (1 << 8),
|
||||
|
||||
SCE_SNAP_MODE_GEOM = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST |
|
||||
SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT |
|
||||
SCE_SNAP_MODE_FACE_NEAREST),
|
||||
|
||||
/** #ToolSettings.snap_node_mode */
|
||||
SCE_SNAP_MODE_NODE_X = (1 << 0),
|
||||
SCE_SNAP_MODE_NODE_Y = (1 << 1),
|
||||
|
@ -2314,6 +2310,10 @@ typedef enum eSnapMode {
|
|||
ENUM_OPERATORS(eSnapMode, SCE_SNAP_MODE_GRID)
|
||||
#endif
|
||||
|
||||
#define SCE_SNAP_MODE_GEOM \
|
||||
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST | \
|
||||
SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_FACE_NEAREST)
|
||||
|
||||
/** #SequencerToolSettings.snap_mode */
|
||||
#define SEQ_SNAP_TO_STRIPS (1 << 0)
|
||||
#define SEQ_SNAP_TO_CURRENT_FRAME (1 << 1)
|
||||
|
|
|
@ -218,6 +218,51 @@ static StructRNA *rna_FModifierType_refine(struct PointerRNA *ptr)
|
|||
# include "DEG_depsgraph.h"
|
||||
# include "DEG_depsgraph_build.h"
|
||||
|
||||
/**
|
||||
* \warning this isn't efficient but it's unavoidable
|
||||
* when only the #ID and the #DriverVar are known.
|
||||
*/
|
||||
static FCurve *rna_FCurve_find_driver_by_variable(ID *owner_id, DriverVar *dvar)
|
||||
{
|
||||
AnimData *adt = BKE_animdata_from_id(owner_id);
|
||||
BLI_assert(adt != NULL);
|
||||
LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
|
||||
ChannelDriver *driver = fcu->driver;
|
||||
if (driver == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (BLI_findindex(&driver->variables, dvar) != -1) {
|
||||
return fcu;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \warning this isn't efficient but it's unavoidable
|
||||
* when only the #ID and the #DriverTarget are known.
|
||||
*/
|
||||
static FCurve *rna_FCurve_find_driver_by_target(ID *owner_id, DriverTarget *dtar)
|
||||
{
|
||||
AnimData *adt = BKE_animdata_from_id(owner_id);
|
||||
BLI_assert(adt != NULL);
|
||||
LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
|
||||
ChannelDriver *driver = fcu->driver;
|
||||
if (driver == NULL) {
|
||||
continue;
|
||||
}
|
||||
LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
|
||||
/* NOTE: Use #MAX_DRIVER_TARGETS instead of `dvar->num_targets` because
|
||||
* it's possible RNA holds a reference to a target that has been removed.
|
||||
* In this case it's best to return the #FCurve it belongs to instead of nothing. */
|
||||
if (ARRAY_HAS_ITEM(dtar, &dvar->targets[0], MAX_DRIVER_TARGETS)) {
|
||||
return fcu;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool rna_ChannelDriver_is_simple_expression_get(PointerRNA *ptr)
|
||||
{
|
||||
ChannelDriver *driver = ptr->data;
|
||||
|
@ -225,20 +270,27 @@ static bool rna_ChannelDriver_is_simple_expression_get(PointerRNA *ptr)
|
|||
return BKE_driver_has_simple_expression(driver);
|
||||
}
|
||||
|
||||
static void rna_ChannelDriver_update_data(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
static void rna_ChannelDriver_update_data_impl(Main *bmain,
|
||||
Scene *scene,
|
||||
ID *owner_id,
|
||||
ChannelDriver *driver)
|
||||
{
|
||||
ID *id = ptr->owner_id;
|
||||
ChannelDriver *driver = ptr->data;
|
||||
|
||||
driver->flag &= ~DRIVER_FLAG_INVALID;
|
||||
|
||||
/* TODO: this really needs an update guard... */
|
||||
DEG_relations_tag_update(bmain);
|
||||
DEG_id_tag_update(id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
||||
DEG_id_tag_update(owner_id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
||||
|
||||
WM_main_add_notifier(NC_SCENE | ND_FRAME, scene);
|
||||
}
|
||||
|
||||
static void rna_ChannelDriver_update_data(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
ID *id = ptr->owner_id;
|
||||
ChannelDriver *driver = ptr->data;
|
||||
rna_ChannelDriver_update_data_impl(bmain, scene, id, driver);
|
||||
}
|
||||
|
||||
static void rna_ChannelDriver_update_expr(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
ChannelDriver *driver = ptr->data;
|
||||
|
@ -252,33 +304,47 @@ static void rna_ChannelDriver_update_expr(Main *bmain, Scene *scene, PointerRNA
|
|||
|
||||
static void rna_DriverTarget_update_data(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
PointerRNA driverptr;
|
||||
ChannelDriver *driver;
|
||||
FCurve *fcu;
|
||||
AnimData *adt = BKE_animdata_from_id(ptr->owner_id);
|
||||
|
||||
/* find the driver this belongs to and update it */
|
||||
for (fcu = adt->drivers.first; fcu; fcu = fcu->next) {
|
||||
driver = fcu->driver;
|
||||
fcu->flag &= ~FCURVE_DISABLED;
|
||||
|
||||
if (driver) {
|
||||
/* FIXME: need to be able to search targets for required one. */
|
||||
// BLI_findindex(&driver->targets, ptr->data) != -1)
|
||||
RNA_pointer_create(ptr->owner_id, &RNA_Driver, driver, &driverptr);
|
||||
rna_ChannelDriver_update_data(bmain, scene, &driverptr);
|
||||
}
|
||||
DriverTarget *dtar = (DriverTarget *)ptr->data;
|
||||
FCurve *fcu = rna_FCurve_find_driver_by_target(ptr->owner_id, dtar);
|
||||
BLI_assert(fcu); /* This hints at an internal error, data may be corrupt. */
|
||||
if (UNLIKELY(fcu == NULL)) {
|
||||
return;
|
||||
}
|
||||
/* Find function ensures it's never NULL. */
|
||||
ChannelDriver *driver = fcu->driver;
|
||||
fcu->flag &= ~FCURVE_DISABLED;
|
||||
rna_ChannelDriver_update_data_impl(bmain, scene, ptr->owner_id, driver);
|
||||
}
|
||||
|
||||
static void rna_DriverTarget_update_name(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
static void rna_DriverVariable_update_name(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
ChannelDriver *driver = ptr->data;
|
||||
rna_DriverTarget_update_data(bmain, scene, ptr);
|
||||
|
||||
DriverVar *dvar = (DriverVar *)ptr->data;
|
||||
FCurve *fcu = rna_FCurve_find_driver_by_variable(ptr->owner_id, dvar);
|
||||
BLI_assert(fcu); /* This hints at an internal error, data may be corrupt. */
|
||||
if (UNLIKELY(fcu == NULL)) {
|
||||
return;
|
||||
}
|
||||
/* Find function ensures it's never NULL. */
|
||||
ChannelDriver *driver = fcu->driver;
|
||||
fcu->flag &= ~FCURVE_DISABLED;
|
||||
rna_ChannelDriver_update_data_impl(bmain, scene, ptr->owner_id, driver);
|
||||
BKE_driver_invalidate_expression(driver, false, true);
|
||||
}
|
||||
|
||||
static void rna_DriverVariable_update_data(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
DriverVar *dvar = (DriverVar *)ptr->data;
|
||||
FCurve *fcu = rna_FCurve_find_driver_by_variable(ptr->owner_id, dvar);
|
||||
BLI_assert(fcu); /* This hints at an internal error, data may be corrupt. */
|
||||
if (UNLIKELY(fcu == NULL)) {
|
||||
return;
|
||||
}
|
||||
/* Find function ensures it's never NULL. */
|
||||
ChannelDriver *driver = fcu->driver;
|
||||
fcu->flag &= ~FCURVE_DISABLED;
|
||||
rna_ChannelDriver_update_data_impl(bmain, scene, ptr->owner_id, driver);
|
||||
}
|
||||
|
||||
/* ----------- */
|
||||
|
||||
/* NOTE: this function exists only to avoid id reference-counting. */
|
||||
|
@ -1951,14 +2017,14 @@ static void rna_def_drivervar(BlenderRNA *brna)
|
|||
"Name",
|
||||
"Name to use in scripted expressions/functions (no spaces or dots are allowed, "
|
||||
"and must start with a letter)");
|
||||
RNA_def_property_update(prop, 0, "rna_DriverTarget_update_name"); /* XXX */
|
||||
RNA_def_property_update(prop, 0, "rna_DriverVariable_update_name");
|
||||
|
||||
/* Enums */
|
||||
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, prop_type_items);
|
||||
RNA_def_property_enum_funcs(prop, NULL, "rna_DriverVariable_type_set", NULL);
|
||||
RNA_def_property_ui_text(prop, "Type", "Driver variable type");
|
||||
RNA_def_property_update(prop, 0, "rna_ChannelDriver_update_data"); /* XXX */
|
||||
RNA_def_property_update(prop, 0, "rna_DriverVariable_update_data");
|
||||
|
||||
/* Targets */
|
||||
/* TODO: for nicer api, only expose the relevant props via subclassing,
|
||||
|
|
|
@ -989,7 +989,11 @@ static PyObject *bpy_bmesh_free(BPy_BMesh *self)
|
|||
|
||||
bm_dealloc_editmode_warn(self);
|
||||
|
||||
if ((self->flag & BPY_BMFLAG_IS_WRAPPED) == 0) {
|
||||
if (self->flag & BPY_BMFLAG_IS_WRAPPED) {
|
||||
/* Ensure further access doesn't return this invalid object, see: #105715. */
|
||||
bm->py_handle = NULL;
|
||||
}
|
||||
else {
|
||||
BM_mesh_free(bm);
|
||||
}
|
||||
|
||||
|
|
|
@ -480,7 +480,7 @@ static PyObject *M_imbuf_load(PyObject *UNUSED(self), PyObject *args, PyObject *
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ImBuf *ibuf = IMB_loadifffile(file, filepath, IB_rect, NULL, filepath);
|
||||
ImBuf *ibuf = IMB_loadifffile(file, IB_rect, NULL, filepath);
|
||||
|
||||
close(file);
|
||||
|
||||
|
|
|
@ -247,6 +247,7 @@ static int count_items(PyObject *seq, int dim)
|
|||
static int validate_array_length(PyObject *rvalue,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
const bool prop_is_param_dyn_alloc,
|
||||
int lvalue_dim,
|
||||
int *r_totitem,
|
||||
const char *error_prefix)
|
||||
|
@ -266,26 +267,20 @@ static int validate_array_length(PyObject *rvalue,
|
|||
return -1;
|
||||
}
|
||||
if ((RNA_property_flag(prop) & PROP_DYNAMIC) && lvalue_dim == 0) {
|
||||
if (RNA_property_array_length(ptr, prop) != tot) {
|
||||
#if 0
|
||||
/* length is flexible */
|
||||
if (!RNA_property_dynamic_array_set_length(ptr, prop, tot)) {
|
||||
/* BLI_snprintf(error_str, error_str_size,
|
||||
* "%s.%s: array length cannot be changed to %d",
|
||||
* RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), tot); */
|
||||
const int tot_expected = RNA_property_array_length(ptr, prop);
|
||||
if (tot_expected != tot) {
|
||||
*r_totitem = tot;
|
||||
if (!prop_is_param_dyn_alloc) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"%s %s.%s: array length cannot be changed to %d",
|
||||
"%s %s.%s: array length cannot be changed to %d (expected %d)",
|
||||
error_prefix,
|
||||
RNA_struct_identifier(ptr->type),
|
||||
RNA_property_identifier(prop),
|
||||
tot);
|
||||
tot,
|
||||
tot_expected);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
*r_totitem = tot;
|
||||
return 0;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
len = tot;
|
||||
|
@ -340,6 +335,7 @@ static int validate_array_length(PyObject *rvalue,
|
|||
static int validate_array(PyObject *rvalue,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
const bool prop_is_param_dyn_alloc,
|
||||
int lvalue_dim,
|
||||
ItemTypeCheckFunc check_item_type,
|
||||
const char *item_type_str,
|
||||
|
@ -410,7 +406,8 @@ static int validate_array(PyObject *rvalue,
|
|||
return -1;
|
||||
}
|
||||
|
||||
return validate_array_length(rvalue, ptr, prop, lvalue_dim, r_totitem, error_prefix);
|
||||
return validate_array_length(
|
||||
rvalue, ptr, prop, prop_is_param_dyn_alloc, lvalue_dim, r_totitem, error_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -527,15 +524,26 @@ static int py_to_array(PyObject *seq,
|
|||
char *data = NULL;
|
||||
|
||||
// totdim = RNA_property_array_dimension(ptr, prop, dim_size); /* UNUSED */
|
||||
const int flag = RNA_property_flag(prop);
|
||||
|
||||
if (validate_array(seq, ptr, prop, 0, check_item_type, item_type_str, &totitem, error_prefix) ==
|
||||
-1) {
|
||||
/* Use #ParameterDynAlloc which defines it's own array length. */
|
||||
const bool prop_is_param_dyn_alloc = param_data && (flag & PROP_DYNAMIC);
|
||||
|
||||
if (validate_array(seq,
|
||||
ptr,
|
||||
prop,
|
||||
prop_is_param_dyn_alloc,
|
||||
0,
|
||||
check_item_type,
|
||||
item_type_str,
|
||||
&totitem,
|
||||
error_prefix) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (totitem) {
|
||||
/* NOTE: this code is confusing. */
|
||||
if (param_data && RNA_property_flag(prop) & PROP_DYNAMIC) {
|
||||
if (prop_is_param_dyn_alloc) {
|
||||
/* not freeing allocated mem, RNA_parameter_list_free() will do this */
|
||||
ParameterDynAlloc *param_alloc = (ParameterDynAlloc *)param_data;
|
||||
param_alloc->array_tot = (int)totitem;
|
||||
|
@ -626,9 +634,16 @@ static int py_to_array_index(PyObject *py,
|
|||
copy_value_single(py, ptr, prop, NULL, 0, &index, convert_item, rna_set_index);
|
||||
}
|
||||
else {
|
||||
if (validate_array(
|
||||
py, ptr, prop, lvalue_dim, check_item_type, item_type_str, &totitem, error_prefix) ==
|
||||
-1) {
|
||||
const bool prop_is_param_dyn_alloc = false;
|
||||
if (validate_array(py,
|
||||
ptr,
|
||||
prop,
|
||||
prop_is_param_dyn_alloc,
|
||||
lvalue_dim,
|
||||
check_item_type,
|
||||
item_type_str,
|
||||
&totitem,
|
||||
error_prefix) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -195,6 +195,76 @@ class TestPropArrayMultiDimensional(unittest.TestCase):
|
|||
del id_type.temp
|
||||
|
||||
|
||||
class TestPropArrayDynamicAssign(unittest.TestCase):
|
||||
"""
|
||||
Pixels are dynamic in the sense the size can change however the assignment does not define the size.
|
||||
"""
|
||||
|
||||
dims = 12
|
||||
|
||||
def setUp(self):
|
||||
self.image = bpy.data.images.new("", self.dims, self.dims)
|
||||
|
||||
def tearDown(self):
|
||||
bpy.data.images.remove(self.image)
|
||||
self.image = None
|
||||
|
||||
def test_assign_fixed_under_1px(self):
|
||||
image = self.image
|
||||
with self.assertRaises(ValueError):
|
||||
image.pixels = [1.0, 1.0, 1.0, 1.0]
|
||||
|
||||
def test_assign_fixed_under_0px(self):
|
||||
image = self.image
|
||||
with self.assertRaises(ValueError):
|
||||
image.pixels = []
|
||||
|
||||
def test_assign_fixed_over_by_1px(self):
|
||||
image = self.image
|
||||
with self.assertRaises(ValueError):
|
||||
image.pixels = ([1.0, 1.0, 1.0, 1.0] * (self.dims * self.dims)) + [1.0]
|
||||
|
||||
def test_assign_fixed(self):
|
||||
# Valid assignment, ensure it works as intended.
|
||||
image = self.image
|
||||
values = [1.0, 0.0, 1.0, 0.0] * (self.dims * self.dims)
|
||||
image.pixels = values
|
||||
self.assertEqual(tuple(values), tuple(image.pixels))
|
||||
|
||||
|
||||
class TestPropArrayDynamicArg(unittest.TestCase):
|
||||
"""
|
||||
Index array, a dynamic array argument which defines it's own length.
|
||||
"""
|
||||
|
||||
dims = 8
|
||||
|
||||
def setUp(self):
|
||||
self.me = bpy.data.meshes.new("")
|
||||
self.me.vertices.add(self.dims)
|
||||
self.ob = bpy.data.objects.new("", self.me)
|
||||
|
||||
def tearDown(self):
|
||||
bpy.data.objects.remove(self.ob)
|
||||
bpy.data.meshes.remove(self.me)
|
||||
self.me = None
|
||||
self.ob = None
|
||||
|
||||
def test_param_dynamic(self):
|
||||
ob = self.ob
|
||||
vg = ob.vertex_groups.new(name="")
|
||||
|
||||
# Add none.
|
||||
vg.add(index=(), weight=1.0, type='REPLACE')
|
||||
for i in range(self.dims):
|
||||
with self.assertRaises(RuntimeError):
|
||||
vg.weight(i)
|
||||
|
||||
# Add all.
|
||||
vg.add(index=range(self.dims), weight=1.0, type='REPLACE')
|
||||
self.assertEqual(tuple([1.0] * self.dims), tuple([vg.weight(i) for i in range(self.dims)]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
|
||||
|
|
Loading…
Reference in New Issue