|
46 | 46 | #include <tuple> |
47 | 47 | #include <map> |
48 | 48 | #include <algorithm> |
| 49 | +#include <cassert> |
49 | 50 |
|
50 | 51 | #ifdef AVS_WINDOWS |
51 | 52 | #include <avs/win.h> |
@@ -191,233 +192,167 @@ AVSValue __cdecl CreateConvertToRGB(AVSValue args, void* user_data, IScriptEnvir |
191 | 192 | // can be overridden later |
192 | 193 | // -1,-2: Planar RGB(A) |
193 | 194 | // 24,32,48,64: RGB24/32/48/64 |
| 195 | + const bool original_target_is_packed = target_rgbtype > 0; // 24,32,48,64 |
| 196 | + |
| 197 | + const int target_bits_per_pixel = args[8].AsInt(vi.BitsPerComponent()); |
| 198 | + // for legacy packed formats it's 8 for target_rgbtype==24,32, and 16 for target_rgbtype==48,64 |
| 199 | + // for planar RGB(A) target, it can be any bit depth supported by the source and the conversion logic (8,10,12,14,16,32) |
194 | 200 |
|
195 | | - const int target_bits_per_pixel = args[8].AsInt(vi.BitsPerComponent()); // only for planar rgb(a) target |
196 | 201 | const bool quality = args[9].AsBool(false); // yuv-planarrgb float workflow |
197 | 202 |
|
198 | 203 | // planar YUV-like source |
199 | 204 | if (vi.IsPlanar() && (vi.IsYUV() || vi.IsYUVA())) { |
200 | | - bool original_target_is_packed = false; |
201 | | - bool needConvertFinalBitdepth = false; |
202 | | - int finalBitdepth = -1; |
203 | 205 | // here we keep the bit depth, later, the RGB conversion will take target_bits_per_pixel into account |
204 | | - AVSValue new_args[10] = { clip, args[2], args[1], args[3], args[4], args[5], args[6], args[7], vi.BitsPerComponent(), quality}; |
| 206 | + AVSValue new_args[10] = { clip, args[2], args[1], args[3], args[4], args[5], args[6], args[7], vi.BitsPerComponent(), quality }; |
205 | 207 | // conversion to planar or packed RGB is always from 444 |
206 | 208 | // clip, interlaced, matrix, chromainplacement, chromaresample, param1, param2, param3, bits, quality Check for ConvertToYUV444 param list!!!! Count must match! |
207 | 209 | clip = ConvertToPlanarGeneric::CreateYUV444(AVSValue(new_args, 10), (void*)1, env).AsClip(); // (void *)1: not restricted to 8 bits |
208 | | - // detect bit-depth change and quality parameter for forced float conversion --> compulsory planar rgb intermediate. |
209 | | - if ((target_rgbtype == 24 || target_rgbtype == 32)) { |
210 | | - if (vi.BitsPerComponent() != 8) { |
211 | | - original_target_is_packed = true; |
212 | | - needConvertFinalBitdepth = true; |
213 | | - finalBitdepth = 8; |
214 | | - target_rgbtype = (target_rgbtype == 24) ? -1 : -2; // planar rgb intermediate |
215 | | - } |
216 | | - else if (quality) { |
217 | | - // even if bit-depth is already 8, quality=true forces float processing and thus planar RGB intermediate |
218 | | - // which handles the parameter automatically |
219 | | - original_target_is_packed = true; |
220 | | - target_rgbtype = (target_rgbtype == 24) ? -1 : -2; // planar rgb intermediate |
221 | | - } |
222 | | - } |
223 | | - else if ((target_rgbtype == 48 || target_rgbtype == 64)) { |
224 | | - if (vi.BitsPerComponent() != 16) { |
225 | | - original_target_is_packed = true; |
226 | | - needConvertFinalBitdepth = true; |
227 | | - finalBitdepth = 16; |
228 | | - target_rgbtype = (target_rgbtype == 48) ? -1 : -2; // planar rgb intermediate |
229 | | - } |
230 | | - else if (quality) { |
231 | | - // even if bit-depth is already 16, quality=true forces float processing and thus planar RGB intermediate |
232 | | - // which handles the parameter automatically |
233 | | - original_target_is_packed = true; |
234 | | - target_rgbtype = (target_rgbtype == 48) ? -1 : -2; // planar rgb intermediate |
235 | | - } |
236 | | - } |
237 | | - else if (target_rgbtype < 0) { |
238 | | - // planar rgb(a) target |
239 | | - if (target_bits_per_pixel != vi.BitsPerComponent()) { |
240 | | - needConvertFinalBitdepth = true; |
241 | | - finalBitdepth = target_bits_per_pixel; |
242 | | - } |
243 | | - } |
244 | | - int rgbtype_param = 0; |
245 | | - bool reallyConvert = true; |
246 | | - bool planar_rgb_conversion_for_packed_rgb_target = false; |
247 | | - |
248 | | - switch (target_rgbtype) |
249 | | - { |
250 | | - case -1: case -2: |
251 | | - rgbtype_param = target_rgbtype; break; // planar RGB(A) |
252 | | - case 24: |
253 | | - rgbtype_param = 3; break; // RGB24 |
254 | | - // Direct YV24->RGB24 by legacy code |
255 | | - case 32: |
256 | | - rgbtype_param = 4; break; // RGB32 |
257 | | - case 48: |
258 | | - case 64: |
259 | | - planar_rgb_conversion_for_packed_rgb_target = true; |
260 | | - break; |
| 210 | + |
| 211 | + // planar RGB(A) target or packed needing intermediate: set finalBitdepth if conversion needed |
| 212 | + // also: when quality==true, we need to use the float workflow which requires a planar RGB(A) intermediate |
| 213 | + if (target_rgbtype > 0) { |
| 214 | + // packed RGB target: redirect to planar rgb(a) intermediate if bit-depth mismatch or quality mode |
| 215 | + const bool hasAlpha = (target_rgbtype == 32 || target_rgbtype == 64); |
| 216 | + if (vi.BitsPerComponent() != target_bits_per_pixel || quality) |
| 217 | + target_rgbtype = hasAlpha ? -2 : -1; |
261 | 218 | } |
262 | 219 |
|
263 | | - if (planar_rgb_conversion_for_packed_rgb_target) { |
| 220 | + if (target_rgbtype == 48 || target_rgbtype == 64) { |
264 | 221 | // Unlike parameter-less RGB24/32, 16 bit packed format have no direct conversions at all. |
265 | 222 | // 1.) YUV->PlanarRGB first (recursive call), |
266 | 223 | // 2.) then fall through to the planar RGB->packed RGB path below for the final repack |
267 | 224 | // So instead of unoptimized code of YUV(A)444P16->RGB48/64 we convert to PlanarRGB(A) then to RGB48/64 |
268 | 225 | // Also: when quality=true or bit-depth conversion is needed, the planar RGB intermediate is required anyway |
269 | 226 | AVSValue new_args2[10] = { clip, args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]/*bits*/, args[9]/*quality*/ }; |
270 | | - const intptr_t target_planar_rgb_type = (target_rgbtype == 48 || target_rgbtype == 24) ? -1 : vi.IsYUVA() ? -2 : -1; // planar RGB or RGBA intermediate |
271 | | - // RGB48 target, planar RGB intermediate with 16 bit components. No alpha. |
272 | | - // RGB64 target, planar RGB or RGBA intermediate with 16 bit components, depending on source alpha |
| 227 | + |
| 228 | + const intptr_t target_planar_rgb_type = |
| 229 | + (target_rgbtype == 24 || target_rgbtype == 48) ? -1 : // no-alpha target: skip alpha even if source has it |
| 230 | + vi.IsYUVA() ? -2 : // alpha target + alpha source: preserve it |
| 231 | + -1; // alpha target + no alpha source: no alpha to copy |
| 232 | + // Note: target_rgbtype == 24 is currently never reached here (24/32 use direct conversion path), |
| 233 | + // but kept for when 24/32-bit packed is also redirected to the planar intermediate path in the future. |
| 234 | + |
273 | 235 | clip = CreateConvertToRGB(AVSValue(new_args2, 10), (void*)target_planar_rgb_type, env).AsClip(); |
274 | 236 | vi = clip->GetVideoInfo(); // must update vi for fall-through |
275 | | - reallyConvert = false; // skip ConvertYUV444ToRGB, fall through |
276 | | - rgbtype_param = |
277 | | - target_rgbtype == 24 ? 3 : |
278 | | - target_rgbtype == 32 ? 4 : |
279 | | - target_rgbtype == 48 ? 6 : |
280 | | - target_rgbtype == 64 ? 8 : 0 ; |
| 237 | + // Fall through to planar RGB source path below to do the final pack |
| 238 | + // reuses the same PlanarRGBtoPackedRGB logic. |
281 | 239 | } |
282 | | - |
283 | | - if (reallyConvert) { |
| 240 | + else { |
| 241 | + // Direct YUV444->RGB conversion path |
284 | 242 | bool bitdepthConverted = false; |
285 | | - if (needConvertFinalBitdepth) { |
286 | | - // Optional bit-depth conversion in PackedRGBtoPlanarRGB. |
287 | | - // int->float full/narrow range, int-int full/narrow supported |
288 | | - clip = new ConvertYUV444ToRGB(clip, matrix_name, rgbtype_param, finalBitdepth, quality, /*ref*/bitdepthConverted, env); |
289 | | - vi = clip->GetVideoInfo(); |
290 | | - if (bitdepthConverted) { |
291 | | - needConvertFinalBitdepth = false; // done in-process |
292 | | - } |
293 | | - } |
294 | | - else { |
295 | | - // pass -1 as finalBitdepth, signing that no bit-depth conversion required |
296 | | - // output bitdepthConverted is n/a |
297 | | - clip = new ConvertYUV444ToRGB(clip, matrix_name, rgbtype_param, -1 /*no bit-depth conversion*/, quality, /*ref*/bitdepthConverted, env); |
298 | | - vi = clip->GetVideoInfo(); |
299 | | - } |
| 243 | + const int finalBitdepth = (vi.BitsPerComponent() != target_bits_per_pixel) ? |
| 244 | + target_bits_per_pixel : -1; // -1 means: no need to convert |
| 245 | + |
| 246 | + // Optional bit-depth conversion in PackedRGBtoPlanarRGB. |
| 247 | + // Note: all formats are bit-depth-converted if finalBitdepth != -1 |
| 248 | + // (historically there were cases and bit-depths which were not implemented to have in-line bit-depth conversion) |
| 249 | + // pass -1 as finalBitdepth, signing that no bit-depth conversion required |
| 250 | + |
| 251 | + const int rgbtype_param = (target_rgbtype == -1 || target_rgbtype == -2) ? target_rgbtype : // planar RGB(A) |
| 252 | + target_rgbtype == 24 ? 3 : 4; // RGB24 or RGB32 "pixel_step" parameter for legacy direct YUV444->RGB24/32 conversion |
| 253 | + |
| 254 | + clip = new ConvertYUV444ToRGB(clip, matrix_name, rgbtype_param, |
| 255 | + finalBitdepth, // -1 if no conversion needed |
| 256 | + quality, /*ref*/bitdepthConverted, env); |
| 257 | + vi = clip->GetVideoInfo(); |
| 258 | + // When direct YUV-RGB24/32 conversion happens, the result is not planar! |
| 259 | + |
| 260 | + const bool needConvertFinalBitdepth = finalBitdepth != -1; |
300 | 261 |
|
301 | | - if (needConvertFinalBitdepth) { |
302 | | - // plain Invoke and no "new ConvertBits()": this detects and keeps source and target ranges |
303 | | - AVSValue new_args[2] = { clip, finalBitdepth }; |
| 262 | + if (needConvertFinalBitdepth && !bitdepthConverted) { |
| 263 | + AVSValue new_args[] = { clip, finalBitdepth }; |
304 | 264 | clip = env->Invoke("ConvertBits", AVSValue(new_args, 2)).AsClip(); |
305 | 265 | vi = clip->GetVideoInfo(); |
306 | 266 | } |
307 | 267 |
|
308 | | - if (original_target_is_packed) { |
| 268 | + // We can still have RGB24/32 here, YV24->24/32 is still valid |
| 269 | + if (original_target_is_packed && vi.IsPlanar()) { |
309 | 270 | // from any planar rgb(a) -> rgb24/32/48/64 |
310 | 271 | // source here is always a 8/16bit planar RGB(A), but we used |
311 | 272 | // planar intermediate. |
312 | 273 | // finally it has to be converted to RGB24/32/48/64 |
313 | | - const bool isRGBA = target_rgbtype == -2; |
314 | | - clip = new PlanarRGBtoPackedRGB(clip, isRGBA); |
| 274 | + clip = new PlanarRGBtoPackedRGB(clip, target_rgbtype == -2); |
315 | 275 | vi = clip->GetVideoInfo(); |
316 | 276 | } |
317 | 277 | return clip; |
318 | 278 | } |
| 279 | + } // end of YUV->RGB path |
| 280 | + else { |
| 281 | + if (haveOpts) |
| 282 | + env->ThrowError("ConvertToRGB: ChromaPlacement and ChromaResample options are not supported."); |
319 | 283 | } |
320 | 284 |
|
321 | | - if (haveOpts) |
322 | | - env->ThrowError("ConvertToRGB: ChromaPlacement and ChromaResample options are not supported."); |
| 285 | + // This part can fallthrough from the YUV path when target is packed RGB |
323 | 286 |
|
324 | | - // planar RGB-like source |
| 287 | + // RGB->RGB paths (planar or packed) do not support ChromaPlacement and ChromaResample options, as they are meaningless for RGB formats. |
| 288 | + |
| 289 | + // Planar RGB(A) source |
325 | 290 | if (vi.IsPlanarRGB() || vi.IsPlanarRGBA()) |
326 | 291 | { |
327 | | - bool needConvertFinalBitdepth = false; |
328 | | - int finalBitdepth = -1; |
329 | | - |
330 | | - if (target_rgbtype < 0) // planar to planar |
331 | | - { |
332 | | - if (vi.IsPlanarRGB()) { |
333 | | - // rgbp->rgbpa create with default alpha |
334 | | - if (target_rgbtype == -2) |
335 | | - clip = new AddAlphaPlane(clip, nullptr, 0.0f, false, env); |
336 | | - } |
337 | | - else { |
338 | | - // planar rgba source |
339 | | - if (target_rgbtype == -1) |
340 | | - clip = new RemoveAlphaPlane(clip, env); |
341 | | - } |
342 | | - if (target_bits_per_pixel != vi.BitsPerComponent()) { |
343 | | - needConvertFinalBitdepth = true; |
344 | | - finalBitdepth = target_bits_per_pixel; |
345 | | - } |
346 | | - } |
347 | | - // planar to packed 24/32/48/64 |
348 | | - else if (target_rgbtype == 24 || target_rgbtype == 32) { |
349 | | - if (vi.BitsPerComponent() != 8) { |
350 | | - needConvertFinalBitdepth = true; |
351 | | - finalBitdepth = 8; |
352 | | - } |
353 | | - } |
354 | | - else if (target_rgbtype == 48 || target_rgbtype == 64) { |
355 | | - if (vi.BitsPerComponent() != 16) { |
356 | | - needConvertFinalBitdepth = true; |
357 | | - finalBitdepth = 16; |
358 | | - } |
| 292 | + // Handle alpha channel add/remove for planar-to-planar |
| 293 | + if (target_rgbtype < 0) { |
| 294 | + if (vi.IsPlanarRGB() && target_rgbtype == -2) |
| 295 | + clip = new AddAlphaPlane(clip, nullptr, 0.0f, false, env); |
| 296 | + else if (vi.IsPlanarRGBA() && target_rgbtype == -1) |
| 297 | + clip = new RemoveAlphaPlane(clip, env); |
359 | 298 | } |
360 | 299 |
|
361 | | - if (needConvertFinalBitdepth) { |
| 300 | + // Convert bit depth if needed |
| 301 | + if (vi.BitsPerComponent() != target_bits_per_pixel) { |
| 302 | + AVSValue args[] = { clip, target_bits_per_pixel }; |
362 | 303 | // plain Invoke instead of "new ConvertBits", this detects and keeps source and target ranges |
363 | | - AVSValue new_args[2] = { clip, finalBitdepth }; |
364 | | - clip = env->Invoke("ConvertBits", AVSValue(new_args, 2)).AsClip(); |
365 | | - vi = clip->GetVideoInfo(); |
| 304 | + clip = env->Invoke("ConvertBits", AVSValue(args, 2)).AsClip(); |
366 | 305 | } |
367 | 306 |
|
| 307 | + // Convert planar to packed |
368 | 308 | if (target_rgbtype >= 0) { |
369 | | - // planar to packed |
370 | | - bool hasAlpha = target_rgbtype == 32 || target_rgbtype == 64 || (target_rgbtype == 0 && vi.IsPlanarRGBA()); |
| 309 | + bool hasAlpha = (target_rgbtype == 32 || target_rgbtype == 64); |
371 | 310 | clip = new PlanarRGBtoPackedRGB(clip, hasAlpha); |
372 | 311 | } |
373 | 312 |
|
374 | 313 | return clip; |
375 | 314 | } // Planar RGB(A) source |
376 | 315 |
|
377 | | - // conversions from packed RGB: |
| 316 | + // YUV source is done |
| 317 | + // Planar RGB(A) source is done |
| 318 | + // Now remains packed RGB source |
| 319 | + // Conversions from packed RGB |
378 | 320 |
|
379 | | - if (target_rgbtype == 24 || target_rgbtype == 32) { |
380 | | - if (vi.ComponentSize() != 1) { |
381 | | - // 64->32, 48->24 |
382 | | - // using Invoke instead of new ConvertBits, this detects and keeps source and target ranges |
383 | | - AVSValue new_args[2] = { clip, 8 }; |
384 | | - clip = env->Invoke("ConvertBits", AVSValue(new_args, 2)).AsClip(); |
385 | | - vi = clip->GetVideoInfo(); // new format |
386 | | - } |
387 | | - } |
388 | | - else if (target_rgbtype == 48 || target_rgbtype == 64) { |
389 | | - if (vi.ComponentSize() != 2) { |
390 | | - // 32->64, 24->48 |
| 321 | + // Packed to Packed |
| 322 | + if (target_rgbtype >= 0) { |
| 323 | + // target bit depth (8-bit for 24/32, 16-bit for 48/64) |
| 324 | + // target_bits_per_pixel is same as target_rgbtype |
| 325 | + |
| 326 | + if (vi.BitsPerComponent() != target_bits_per_pixel) { |
| 327 | + AVSValue args[] = { clip, target_bits_per_pixel }; |
391 | 328 | // using Invoke instead of new ConvertBits, this detects and keeps source and target ranges |
392 | | - AVSValue new_args[2] = { clip, 16 }; |
393 | | - clip = env->Invoke("ConvertBits", AVSValue(new_args, 2)).AsClip(); |
394 | | - vi = clip->GetVideoInfo(); // new format |
| 329 | + clip = env->Invoke("ConvertBits", AVSValue(args, 2)).AsClip(); |
| 330 | + vi = clip->GetVideoInfo(); |
395 | 331 | } |
| 332 | + |
| 333 | + bool target_has_alpha = (target_rgbtype == 32 || target_rgbtype == 64); |
| 334 | + bool source_has_alpha = (vi.IsRGB32() || vi.IsRGB64()); |
| 335 | + |
| 336 | + // between packed RGB types, alpha add/remove |
| 337 | + if (target_has_alpha && !source_has_alpha) |
| 338 | + return new RGBtoRGBA(clip); |
| 339 | + if (!target_has_alpha && source_has_alpha) |
| 340 | + return new RGBAtoRGB(clip); |
| 341 | + |
| 342 | + return clip; |
396 | 343 | } |
397 | 344 |
|
398 | | - // between packed RGB types, alpha add |
399 | | - if (target_rgbtype == 32 || target_rgbtype == 64) |
400 | | - if (vi.IsRGB24() || vi.IsRGB48()) |
401 | | - return new RGBtoRGBA(clip); // 24->32 or 48->64 |
402 | | - |
403 | | - // between packed RGB types, alpha remove |
404 | | - if (target_rgbtype == 24 || target_rgbtype == 48) |
405 | | - if (vi.IsRGB32() || vi.IsRGB64()) |
406 | | - return new RGBAtoRGB(clip); // 32->24 or 64->48 |
407 | | - |
408 | | - // <0: target is planar RGB(A) |
409 | | - if (target_rgbtype < 0) { |
410 | | - // RGB24/32/48/64 -> |
411 | | - const bool isSrcRGBA = vi.IsRGB32() || vi.IsRGB64(); |
412 | | - const bool isTargetRGBA = target_rgbtype == -2; |
413 | | - clip = new PackedRGBtoPlanarRGB(clip, isSrcRGBA, isTargetRGBA); |
| 345 | + // Packed to Planar |
| 346 | + // RGB24/32/48/64 -> |
| 347 | + const bool isSrcRGBA = vi.IsRGB32() || vi.IsRGB64(); |
| 348 | + const bool isTargetRGBA = target_rgbtype == -2; // -2 planar RGBA, -1 planar RGB |
| 349 | + clip = new PackedRGBtoPlanarRGB(clip, isSrcRGBA, isTargetRGBA); |
| 350 | + vi = clip->GetVideoInfo(); // new format |
| 351 | + // no embedded bitdepth conversion in PackedRGBtoPlanarRGB |
| 352 | + if (target_bits_per_pixel != vi.BitsPerComponent()) { |
| 353 | + AVSValue new_args[2] = { clip, target_bits_per_pixel }; |
| 354 | + clip = env->Invoke("ConvertBits", AVSValue(new_args, 2)).AsClip(); |
414 | 355 | vi = clip->GetVideoInfo(); // new format |
415 | | - // no embedded bitdepth conversion in PackedRGBtoPlanarRGB |
416 | | - if (target_bits_per_pixel != vi.BitsPerComponent()) { |
417 | | - AVSValue new_args[2] = { clip, target_bits_per_pixel }; |
418 | | - clip = env->Invoke("ConvertBits", AVSValue(new_args, 2)).AsClip(); |
419 | | - vi = clip->GetVideoInfo(); // new format |
420 | | - } |
421 | 356 | } |
422 | 357 |
|
423 | 358 | return clip; |
|
0 commit comments