Skip to content

Commit 7b21d00

Browse files
committed
CreateConvertToRGB cleanup
1 parent 9d0ae3a commit 7b21d00

1 file changed

Lines changed: 105 additions & 170 deletions

File tree

avs_core/convert/convert.cpp

Lines changed: 105 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <tuple>
4747
#include <map>
4848
#include <algorithm>
49+
#include <cassert>
4950

5051
#ifdef AVS_WINDOWS
5152
#include <avs/win.h>
@@ -191,233 +192,167 @@ AVSValue __cdecl CreateConvertToRGB(AVSValue args, void* user_data, IScriptEnvir
191192
// can be overridden later
192193
// -1,-2: Planar RGB(A)
193194
// 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)
194200

195-
const int target_bits_per_pixel = args[8].AsInt(vi.BitsPerComponent()); // only for planar rgb(a) target
196201
const bool quality = args[9].AsBool(false); // yuv-planarrgb float workflow
197202

198203
// planar YUV-like source
199204
if (vi.IsPlanar() && (vi.IsYUV() || vi.IsYUVA())) {
200-
bool original_target_is_packed = false;
201-
bool needConvertFinalBitdepth = false;
202-
int finalBitdepth = -1;
203205
// 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 };
205207
// conversion to planar or packed RGB is always from 444
206208
// clip, interlaced, matrix, chromainplacement, chromaresample, param1, param2, param3, bits, quality Check for ConvertToYUV444 param list!!!! Count must match!
207209
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;
261218
}
262219

263-
if (planar_rgb_conversion_for_packed_rgb_target) {
220+
if (target_rgbtype == 48 || target_rgbtype == 64) {
264221
// Unlike parameter-less RGB24/32, 16 bit packed format have no direct conversions at all.
265222
// 1.) YUV->PlanarRGB first (recursive call),
266223
// 2.) then fall through to the planar RGB->packed RGB path below for the final repack
267224
// So instead of unoptimized code of YUV(A)444P16->RGB48/64 we convert to PlanarRGB(A) then to RGB48/64
268225
// Also: when quality=true or bit-depth conversion is needed, the planar RGB intermediate is required anyway
269226
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+
273235
clip = CreateConvertToRGB(AVSValue(new_args2, 10), (void*)target_planar_rgb_type, env).AsClip();
274236
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.
281239
}
282-
283-
if (reallyConvert) {
240+
else {
241+
// Direct YUV444->RGB conversion path
284242
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;
300261

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 };
304264
clip = env->Invoke("ConvertBits", AVSValue(new_args, 2)).AsClip();
305265
vi = clip->GetVideoInfo();
306266
}
307267

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()) {
309270
// from any planar rgb(a) -> rgb24/32/48/64
310271
// source here is always a 8/16bit planar RGB(A), but we used
311272
// planar intermediate.
312273
// 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);
315275
vi = clip->GetVideoInfo();
316276
}
317277
return clip;
318278
}
279+
} // end of YUV->RGB path
280+
else {
281+
if (haveOpts)
282+
env->ThrowError("ConvertToRGB: ChromaPlacement and ChromaResample options are not supported.");
319283
}
320284

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
323286

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
325290
if (vi.IsPlanarRGB() || vi.IsPlanarRGBA())
326291
{
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);
359298
}
360299

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 };
362303
// 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();
366305
}
367306

307+
// Convert planar to packed
368308
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);
371310
clip = new PlanarRGBtoPackedRGB(clip, hasAlpha);
372311
}
373312

374313
return clip;
375314
} // Planar RGB(A) source
376315

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
378320

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 };
391328
// 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();
395331
}
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;
396343
}
397344

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();
414355
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-
}
421356
}
422357

423358
return clip;

0 commit comments

Comments
 (0)