| 205 | } |
| 206 | |
| 207 | int RemoveInverseOps(OpRcPtrVec & opVec, OptimizationFlags oFlags) |
| 208 | { |
| 209 | int count = 0; |
| 210 | int firstindex = 0; // this must be a signed int |
| 211 | |
| 212 | while (firstindex < (static_cast<int>(opVec.size()) - 1)) |
| 213 | { |
| 214 | ConstOpRcPtr op1 = opVec[firstindex]; |
| 215 | ConstOpRcPtr op2 = opVec[firstindex + 1]; |
| 216 | const auto type1 = op1->data()->getType(); |
| 217 | const auto type2 = op2->data()->getType(); |
| 218 | // The common case of inverse ops is to have a deep nesting: |
| 219 | // ..., A, B, B', A', ... |
| 220 | // |
| 221 | // Consider the above, when firstindex reaches B: |
| 222 | // |
| 223 | // | |
| 224 | // ..., A, B, B', A', ... |
| 225 | // |
| 226 | // We will remove B and B'. |
| 227 | // Firstindex remains pointing at the original location: |
| 228 | // |
| 229 | // | |
| 230 | // ..., A, A', ... |
| 231 | // |
| 232 | // We then decrement firstindex by 1, |
| 233 | // to backstep and reconsider the A, A' case: |
| 234 | // |
| 235 | // | <-- firstindex decremented |
| 236 | // ..., A, A', ... |
| 237 | // |
| 238 | |
| 239 | if (type1 == type2 && |
| 240 | IsPairInverseEnabled(type1, oFlags) && |
| 241 | op1->isInverse(op2)) |
| 242 | { |
| 243 | // When a pair of inverse ops is removed, we want the optimized ops to give the |
| 244 | // same result as the original. For certain ops such as Lut1D or Log this may |
| 245 | // mean inserting a Range to emulate the clamping done by the original ops. |
| 246 | |
| 247 | OpRcPtr replacedBy; |
| 248 | if (type1 == OpData::Lut1DType) |
| 249 | { |
| 250 | // Lut1D gets special handling so that both halfs of the pair are available. |
| 251 | // Only the inverse LUT has the values needed to generate the replacement. |
| 252 | |
| 253 | ConstLut1DOpDataRcPtr lut1 = OCIO_DYNAMIC_POINTER_CAST<const Lut1DOpData>(op1->data()); |
| 254 | ConstLut1DOpDataRcPtr lut2 = OCIO_DYNAMIC_POINTER_CAST<const Lut1DOpData>(op2->data()); |
| 255 | |
| 256 | OpDataRcPtr opData = lut1->getPairIdentityReplacement(lut2); |
| 257 | |
| 258 | OpRcPtrVec ops; |
| 259 | if (opData->getType() == OpData::MatrixType) |
| 260 | { |
| 261 | // No-op that will be optimized. |
| 262 | auto mat = OCIO_DYNAMIC_POINTER_CAST<MatrixOpData>(opData); |
| 263 | CreateMatrixOp(ops, mat, TRANSFORM_DIR_FORWARD); |
| 264 | } |