| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2022-2024 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / AVGenerator filter | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| import { Bitstream as BS } from 'gpaccore' | |
| import { Sys as sys } from 'gpaccore' | |
| import * as evg from 'evg' | |
| filter.pids = []; | |
| const SAMPLING_NONE=0; | |
| const SAMPLING_422=1; | |
| const SAMPLING_420=2; | |
| const SAMPLING_411=3; | |
| const INTERLEAVE_COMPONENT=0; | |
| const INTERLEAVE_PIXEL=1; | |
| const INTERLEAVE_MIXED=2; | |
| const INTERLEAVE_ROW=3; | |
| const INTERLEAVE_TILE=4; | |
| const INTERLEAVE_MULTIY=5; | |
| //metadata | |
| filter.set_name("uncvg"); | |
| filter.set_class_hint(GF_FS_CLASS_MM_IO); | |
| filter.set_desc("Uncompressed Video Generator"); | |
| filter.set_version("1.0"); | |
| filter.set_author("GPAC team - (c) Telecom ParisTech 2023 - license LGPL v2"); | |
| filter.set_help("This filter provides generation of test images for ISO/IEC 23001-17\n" | |
| +"Generated pixels can be:\n" | |
| +"- a pattern of colors in columns, or in rectangles if [-sq]() is set, using the specified palette.\n" | |
| +"- an image source if [-img]() is used.\n" | |
| +"\n" | |
| +"Colors specified in the palette can be default GPAC colors (see gpac -h colors), 0xRRGGBB or 0xAARRGGBB\n" | |
| +"\n" | |
| +"When generating video, the pixels are shifted to the left at every frame\n" | |
| +"\n" | |
| +"Components are described as N[bpc][+k] with\n" | |
| +"- N: component type, one of M(mono), Y, U, V, R, G, B, A, d(depth), disp, p(palette), f(filterArray), x (pad)\n" | |
| +"- bpc: bits per component value, default is 8. Non-integer values must be one of\n" | |
| +" - sft: floating-point value on 16 bits\n" | |
| +" - flt: floating-point value on 32 bits\n" | |
| +" - dbl: floating-point value on 64 bits\n" | |
| +" - dblx: floating-point value on 128 bits\n" | |
| +" - cps: complex value as two floats on 16 bits each\n" | |
| +" - cpf: complex value as two floats on 32 bits each\n" | |
| +" - cpd: complex value as two floats on 64 bits each\n" | |
| +" - cpx: complex value as two floats on 128 bits each\n" | |
| +"- k: force component alignment on k bytes, default is 0 (no alignment)\n" | |
| ); | |
| filter.set_arg({ name: "vsize", desc: "width and height of output image", type: GF_PROP_VEC2, def: "128x128"} ); | |
| filter.set_arg({ name: "c", desc: "image components", type: GF_PROP_STRING_LIST, def: ["R8", "G8", "B8"]} ); | |
| filter.set_arg({ name: "tiles", desc: "number of horizontal and vertical tiles", type: GF_PROP_VEC2, def: "1x1"} ); | |
| filter.set_arg({ name: "interleave", desc: `interleave type | |
| - comp: component-based interleaving (planar modes) | |
| - pix: pixel-based interleaving (packed modes) | |
| - mix: pixel-based interleaving for UV (semi-planar modes) | |
| - row: row-based interleaving | |
| - tile: tile-component interleaving | |
| - multi: pixel-based interleaving (packed modes) for sub-sampled modes`, type: GF_PROP_UINT, def: "pix", minmax_enum: "comp|pix|mix|row|tile|multi"} ); | |
| filter.set_arg({ name: "sampling", desc: `sampling types | |
| - none: no sub-sampling | |
| - 422: YUV 4:2:2 sub-sampling | |
| - 420: YUV 4:2:0 sub-sampling | |
| - 411: YUV 4:1:1 sub-sampling`, type: GF_PROP_UINT, def: "none", minmax_enum: "none|422|420|411"} ); | |
| filter.set_arg({ name: "block_size", desc: "block size in bytes", type: GF_PROP_UINT, def: "0"} ); | |
| filter.set_arg({ name: "pad_lsb", desc: "padded bits are at LSB in the block", type: GF_PROP_BOOL, def: "false"} ); | |
| filter.set_arg({ name: "ble", desc: "block is little endian", type: GF_PROP_BOOL, def: "false"} ); | |
| filter.set_arg({ name: "br", desc: "block has reversed components", type: GF_PROP_BOOL, def: "false"} ); | |
| filter.set_arg({ name: "pixel_size", desc: "size of pixel in bytes", type: GF_PROP_UINT, def: "0"} ); | |
| filter.set_arg({ name: "row_align", desc: "row alignment in bytes", type: GF_PROP_UINT, def: "0"} ); | |
| filter.set_arg({ name: "tile_align", desc: "tile alignment in bytes", type: GF_PROP_UINT, def: "0"} ); | |
| filter.set_arg({ name: "cle", desc: "byte-aligned components are little endian", type: GF_PROP_BOOL, def: "false"} ); | |
| filter.set_arg({ name: "img", desc: "use specified image as input instead of RGB generation", type: GF_PROP_STRING, def: ""} ); | |
| filter.set_arg({ name: "asize", desc: "use input image size", type: GF_PROP_BOOL, def: "false"} ); | |
| filter.set_arg({ name: "pal", desc: "default palette for color generation", type: GF_PROP_STRING_LIST, def: ["red", "green", "blue", "white", "black", "yellow", "cyan", "grey", "orange", "violet"]} ); | |
| filter.set_arg({ name: "fa", desc: "bayer-like filter - only 2x2 on R,G,B components is supported", type: GF_PROP_STRING_LIST, def: ["B", "G", "G", "R"]} ); | |
| filter.set_arg({ name: "bpm", desc: "set sensor bad pixel map as a list of cN (broken column), rM (broken row) or NxM (single pixel)", type: GF_PROP_STRING_LIST, def: []} ); | |
| filter.set_arg({ name: "fps", desc: "frame rate to generate - using 0 will trigger item muxing", type: GF_PROP_FRACTION, def: "25/1"} ); | |
| filter.set_arg({ name: "dur", desc: "duration to generate - using 0 will trigger item muxing", type: GF_PROP_FRACTION, def: "1/1"} ); | |
| filter.set_arg({ name: "cloc", desc: "set chroma location type", type: GF_PROP_UINT, def: "-1", minmax_enum: "-1,6"} ); | |
| filter.set_arg({ name: "stereo", desc: "dump a stereo image", type: GF_PROP_BOOL, def: "false"} ); | |
| filter.set_arg({ name: "sq", desc: "generate square patterns instead of columns", type: GF_PROP_FLOAT, def: "0"} ); | |
| filter.set_arg({ name: "shade", desc: "shade pixels from black at bottom to full intensity at top", type: GF_PROP_BOOL, def: "false"} ); | |
| filter.set_arg({ name: "scpt", desc: "use single color per tile, first tile using the first color in palette", type: GF_PROP_BOOL, def: "false"} ); | |
| let components = []; | |
| let tx = null; | |
| let tx_width, tx_height; | |
| let col_width; | |
| let out_size=0; | |
| let cts=0; | |
| let max_cts=0; | |
| let pid; | |
| let pixel = []; | |
| let min_bits=0; | |
| let pix_len; | |
| let r_y_idx = -1; | |
| let g_u_idx = -1; | |
| let b_v_idx = -1; | |
| let a_idx = -1; | |
| let d_idx = -1; | |
| let is_playing=false; | |
| let use_palette=false; | |
| let use_fa=false; | |
| let fa_width=2; | |
| let fa_height=2; | |
| let fa=[]; | |
| let palette_alpha=false; | |
| filter.set_cap({id: "StreamType", value: "Video", output: true} ); | |
| filter.set_cap({id: "CodecID", value: "raw", output: true} ); | |
| filter.initialize = function() | |
| { | |
| /*create a texture from a local file*/ | |
| if (this.img != "") { | |
| tx = new evg.Texture(this.img); | |
| tx_width = tx.width; | |
| tx_height = tx.height; | |
| if (this.asize) { | |
| this.vsize.x = tx.width; | |
| this.vsize.y = tx.height; | |
| } | |
| } | |
| col_width = Math.ceil(filter.vsize.x / filter.pal.length); | |
| while (col_width%4) col_width++; | |
| if (filter.stereo && (filter.vsize.x%2)) | |
| filter.vsize.x++; | |
| setup_uncv(); | |
| pid = this.new_pid(); | |
| pid.set_prop('StreamType', 'Visual'); | |
| pid.set_prop('CodecID', 'uncv'); | |
| pid.set_prop('Width', this.vsize.x); | |
| pid.set_prop('Height', this.vsize.y); | |
| pid.set_prop('Timescale', filter.fps.n ? filter.fps.n : 1000); | |
| pid.set_prop('PixelFormat', 'uncv'); | |
| if (!filter.fps.n || !filter.fps.d || !filter.dur.n || !filter.dur.d) { | |
| max_cts = 1; | |
| pid.set_prop('ItemID', 1); | |
| } else { | |
| if (filter.fps.n && filter.fps.d) | |
| pid.set_prop('FPS', filter.fps); | |
| max_cts = filter.dur.n * filter.fps.n / filter.dur.d / filter.fps.d; | |
| } | |
| //write dec config as a box array | |
| let bs = new BS(); | |
| let size, pos = 0; | |
| //component definition | |
| bs.put_u32(0); | |
| bs.put_4cc("cmpd"); | |
| if (use_palette || use_fa) { | |
| if (palette_alpha) | |
| bs.put_u32(components.length+4); | |
| else | |
| bs.put_u32(components.length+3); | |
| } | |
| else | |
| bs.put_u32(components.length); | |
| for (let i=0; i<components.length; i++) { | |
| let c = components[i]; | |
| bs.put_u16(c.type); | |
| } | |
| if (use_palette || use_fa) { | |
| bs.put_u16(4); //R | |
| bs.put_u16(5); //G | |
| bs.put_u16(6); //B | |
| if (palette_alpha) | |
| bs.put_u16(7); //A | |
| } | |
| size = bs.pos; | |
| bs.pos = 0; | |
| bs.put_u32(size); | |
| bs.pos = size; | |
| //uncC | |
| pos = bs.pos; | |
| bs.put_u32(0); | |
| bs.put_4cc("uncC"); | |
| bs.put_u32(0); //version+flags | |
| bs.put_u32(0); //profile | |
| bs.put_u32(components.length); | |
| for (let i=0; i<components.length; i++) { | |
| let c = components[i]; | |
| bs.put_u16(i); //index | |
| bs.put_u8(c.bits-1); //nb_bits_minus_one | |
| bs.put_u8(c.mode); //component_format | |
| bs.put_u8(c.align); //component_align_size | |
| } | |
| bs.put_u8(filter.sampling); | |
| bs.put_u8(filter.interleave); | |
| bs.put_u8(filter.block_size); | |
| bs.put_bits(filter.cle, 1); //components_little_endian | |
| if (filter.block_size) { | |
| bs.put_bits(filter.pad_lsb ? 1 : 0, 1); //pad_lsb | |
| bs.put_bits(filter.ble ? 1 : 0, 1); //block_little_endian | |
| bs.put_bits(filter.br ? 1 : 0, 1); //block_reversed | |
| } else { | |
| bs.put_bits(0, 3); | |
| } | |
| bs.put_bits(0, 1); //pad_unknown | |
| bs.put_bits(0, 3); //reserved | |
| bs.put_u32(filter.pixel_size); | |
| bs.put_u32(filter.row_align); | |
| bs.put_u32(filter.tile_align); | |
| bs.put_u32(filter.tiles.x-1); | |
| bs.put_u32(filter.tiles.y-1); | |
| //write size | |
| size = bs.pos - pos; | |
| bs.pos = pos; | |
| bs.put_u32(size); | |
| bs.pos = size + pos; | |
| if (use_palette) { | |
| pos = bs.pos; | |
| bs.put_u32(0); | |
| bs.put_4cc("cpal"); | |
| bs.put_u32(0); //version 0 no flags | |
| bs.put_u16(palette_alpha ? 4 : 3); //nb_comp, only (A)RGB palette 8 bits int for now | |
| bs.put_u32(components.length); //R idx | |
| bs.put_u8(7); //bits minus 1 | |
| bs.put_u8(0); //format | |
| bs.put_u32(components.length+1); //G idx | |
| bs.put_u8(7); //bits minus 1 | |
| bs.put_u8(0); //format | |
| bs.put_u32(components.length+2); //B idx | |
| bs.put_u8(7); //bits minus 1 | |
| bs.put_u8(0); //format | |
| if (palette_alpha) { | |
| bs.put_u32(components.length+3); //A idx | |
| bs.put_u8(7); //bits minus 1 | |
| bs.put_u8(0); //format | |
| } | |
| bs.put_u32(filter.pal.length); | |
| for (let i=0; i<filter.pal.length; i++) { | |
| let col = filter.pal[i]; | |
| bs.put_u8( sys.color_component(col, 1) * 255); | |
| bs.put_u8( sys.color_component(col, 2) * 255 ); | |
| bs.put_u8( sys.color_component(col, 3) * 255 ); | |
| if (palette_alpha) | |
| bs.put_u8( sys.color_component(col, 0) * 255 ); | |
| } | |
| //write size | |
| let size = bs.pos - pos; | |
| bs.pos = pos; | |
| bs.put_u32(size); | |
| bs.pos = size + pos; | |
| } | |
| if (use_fa) { | |
| pos = bs.pos; | |
| bs.put_u32(0); | |
| bs.put_4cc("cpat"); | |
| bs.put_u32(0); //version 0 no flags | |
| bs.put_u16(fa_width); //pattern width | |
| bs.put_u16(fa_height); //pattern height | |
| for (let i=0; i<fa_height; i++) { | |
| for (let j=0; j<fa_width; j++) { | |
| let ctype = fa[i*fa_height + j]; | |
| //ctype is 4->6 (R->B), so index us ctype-4 + components.length | |
| bs.put_u32(ctype-4 + components.length); | |
| bs.put_float(1.0); | |
| } | |
| } | |
| //write size | |
| let size = bs.pos - pos; | |
| bs.pos = pos; | |
| bs.put_u32(size); | |
| bs.pos = size + pos; | |
| } | |
| let bad_rows=[]; | |
| let bad_cols=[]; | |
| let bad_pix=[]; | |
| let use_sbpm=false; | |
| for (let i=0; i<filter.bpm.length; i++) { | |
| let c = filter.bpm[i]; | |
| if (c.indexOf('r')==0) { | |
| bad_rows.push( parseInt(c.slice(1) ) ); | |
| use_sbpm=true; | |
| } | |
| else if (c.indexOf('c')==0) { | |
| bad_cols.push( parseInt(c.slice(1) ) ); | |
| use_sbpm=true; | |
| } | |
| else { | |
| let s = c.indexOf('x'); | |
| if (s>0) { | |
| let x = parseInt(c.slice(0, s) ); | |
| let y = parseInt(c.slice(s+1) ); | |
| bad_pix.push({x: x, y: y}); | |
| use_sbpm=true; | |
| } | |
| } | |
| } | |
| if (use_sbpm) { | |
| pos = bs.pos; | |
| bs.put_u32(0); | |
| bs.put_4cc("sbpm"); | |
| bs.put_u32(0); //version 0 no flags | |
| bs.put_u32(0); //component count, applies to all | |
| bs.put_u8(0); //correction applied + reserved7=0 | |
| bs.put_u32(bad_rows.length); | |
| bs.put_u32(bad_cols.length); | |
| bs.put_u32(bad_pix.length); | |
| for (let i=0; i<bad_rows.length; i++) { | |
| bs.put_u32(bad_rows[i]); | |
| } | |
| for (let i=0; i<bad_cols.length; i++) { | |
| bs.put_u32(bad_cols[i]); | |
| } | |
| for (let i=0; i<bad_pix.length; i++) { | |
| bs.put_u32(bad_pix[i].y); //bad row | |
| bs.put_u32(bad_pix[i].x); //bad col | |
| } | |
| //write size | |
| let size = bs.pos - pos; | |
| bs.pos = pos; | |
| bs.put_u32(size); | |
| bs.pos = size + pos; | |
| } | |
| if ((filter.cloc>=0) && gen_yuv) { | |
| bs.put_u32(13); | |
| bs.put_4cc("cloc"); | |
| bs.put_u32(0); //version 0 no flags | |
| bs.put_u8(filter.cloc); | |
| } | |
| if (filter.stereo) { | |
| bs.put_u32(14); | |
| bs.put_4cc("fpac"); | |
| bs.put_u32(0); //version 0 no flags | |
| bs.put_bits(3, 4); //video_frame_packing side by side | |
| bs.put_bits(1, 4); //PackedContentInterpretationType left is left view | |
| bs.put_bits(0, 1); //QuincunxSamplingFlag | |
| bs.put_bits(0, 7); //reserved | |
| } | |
| /* todo - write if needed | |
| NUC | |
| polarization | |
| reference level | |
| disparity info | |
| depth mapping info | |
| */ | |
| pid.set_prop('DecoderConfig', bs.get_content()); | |
| /*todo - set field interlace sample group if needed*/ | |
| } | |
| let blocksize_bits; | |
| let gen_yuv; | |
| function line_size(comp_bits) | |
| { | |
| let clen = comp_bits.length; | |
| let size=0; | |
| if (!filter.block_size) { | |
| for (let j=0; j<tile_width; j++) { | |
| for (let c=0; c<clen; c++) { | |
| let cbits; | |
| if (comp_bits[c].align) { | |
| cbits = 8*comp_bits[c].align; | |
| while (size%8) size++; | |
| } else { | |
| cbits = comp_bits[c].bits; | |
| } | |
| size += cbits; | |
| } | |
| } | |
| while (size % 8) size++; | |
| return size / 8; | |
| } | |
| //block size, figure out how many blocks | |
| let nb_bits = 0; | |
| for (let i=0; i<tile_width; i++) { | |
| for (let c=0; c<clen; c++) { | |
| let cbits; | |
| if (comp_bits[c].align) { | |
| cbits = 8*comp_bits[c].align; | |
| while (size%8) size++; | |
| } else { | |
| cbits = comp_bits[c].bits; | |
| } | |
| if (nb_bits + cbits > blocksize_bits) { | |
| nb_bits=0; | |
| size += filter.block_size; | |
| //if first comp in block is first comp, pattern is done | |
| if (c==0) { | |
| let nb_pix = i; | |
| let nb_blocks = 1; | |
| while ((nb_blocks+1) * nb_pix < tile_width) nb_blocks++; | |
| size *= nb_blocks; | |
| //jump to last non-full pattern | |
| i += nb_pix * (nb_blocks-1); | |
| } | |
| } | |
| nb_bits += cbits; | |
| } | |
| } | |
| if (nb_bits) | |
| size += filter.block_size; | |
| return size; | |
| } | |
| let tile_width, tile_height; | |
| let row_align_bytes, tile_align_bytes, subsample_x; | |
| const comp_types = [ | |
| { type: 8, name: "depth", alias: "d"}, | |
| { type: 9, name: "disp"}, | |
| { type: 10, name: "pal", alias: "p"}, | |
| { type: 11, name: "fa", alias: "f"}, | |
| { type: 12, name: "pad", alias: "x"}, | |
| //do not change order of these types otherwise we conflict with 'u', 'v' and 'g' | |
| { type: 0, name: "mono", alias: "m"}, | |
| { type: 1, name: "y"}, | |
| { type: 2, name: "u"}, | |
| { type: 3, name: "v"}, | |
| { type: 4, name: "r"}, | |
| { type: 5, name: "g"}, | |
| { type: 6, name: "b"}, | |
| { type: 7, name: "a"}, | |
| ]; | |
| let comp_le_buf = null; | |
| let comp_le_bs = null; | |
| let comp_le_view = null; | |
| let has_a=false; | |
| let has_depth=false; | |
| let has_disp=false; | |
| function setup_uncv() | |
| { | |
| components.length=0; | |
| if (filter.c.length==0) | |
| throw "At least one component must be specified"; | |
| let max_align_size=0; | |
| let has_rgb=false; | |
| for (let i=0; i<filter.pal.length; i++) { | |
| if (sys.color_component(filter.pal[i], 0) < 1.0) { | |
| palette_alpha = true; | |
| break; | |
| } | |
| } | |
| for (let i=0; i<filter.c.length; i++) { | |
| let depth=0; | |
| let fmt=0; | |
| let c_str = filter.c[i].toLowerCase(); | |
| let type=0; | |
| for (let j=0; j<comp_types.length; j++) { | |
| let match=0; | |
| if (c_str.startsWith(comp_types[j].name)) { | |
| match = comp_types[j].name.length; | |
| } | |
| else if (!match && (typeof comp_types[j].alias == 'string') && c_str.startsWith(comp_types[j].alias)) { | |
| match = comp_types[j].alias.length; | |
| } | |
| if (match) { | |
| type = comp_types[j].type + 1; | |
| if (typeof comp_types[j].fmt != 'undefined') | |
| fmt = comp_types[j].fmt; | |
| c_str = c_str.slice(match); | |
| break; | |
| } | |
| } | |
| if (!type) { | |
| throw "Invalid component declaration "+c_str; | |
| } | |
| type--; | |
| let align_size=0; | |
| let ws = c_str.indexOf('+'); | |
| if (ws>0) { | |
| let a_str = c_str.slice(ws + 1); | |
| align_size = parseInt(a_str); | |
| c_str = c_str.slice(0, ws); | |
| if (align_size>max_align_size) max_align_size = align_size; | |
| } | |
| if (c_str=='') { fmt=0; depth = 8; } | |
| else if (c_str=='flt') {fmt = 1; depth=32; } | |
| else if (c_str=='sft') {fmt = 1; depth=16; } | |
| else if (c_str=='dbl') {fmt = 1; depth=64; } | |
| else if (c_str=='dbx') {fmt = 1; depth=128; } | |
| else if (c_str=='cps') {fmt = 2; depth=32; } | |
| else if (c_str=='cpf') {fmt = 2; depth=64; } | |
| else if (c_str=='cpd') {fmt = 2; depth=128; } | |
| else if (c_str=='cpx') {fmt = 2; depth=256; } | |
| else if (!isNaN(c_str)) { fmt=0; depth = parseInt(c_str); } | |
| if (!depth) { | |
| throw "Invalid component declaration "+c_str; | |
| } | |
| if (fmt==2) { | |
| throw "Complex types not supported yet"; | |
| } | |
| if (align_size && (align_size*8<depth)) { | |
| throw "Align size must be greater than component bit depth"; | |
| } | |
| components.push({type: type, bits: depth, mode: fmt, align: align_size}); | |
| if (type==10) use_palette=true; | |
| else if (type==11) use_fa=true; | |
| else if ((type>=4) && (type<=6)) { | |
| has_rgb = true; | |
| } | |
| else if (type==7) { | |
| palette_alpha = false; | |
| has_a=true; | |
| } | |
| else if (type==8) has_depth = true; | |
| else if (type==9) has_disp = true; | |
| } | |
| if (use_palette && has_rgb) { | |
| throw "Cannot use R,G or B component with palette (already RGB)"+c_str; | |
| } | |
| if (use_fa) { | |
| if (has_rgb) { | |
| throw "Cannot use R,G or B component with filter array (already RGB)"+c_str; | |
| } | |
| if (use_palette) { | |
| throw "Cannot use palette with filter array"+c_str; | |
| } | |
| if (filter.fa.length != 4) { | |
| throw "Filter array must be 2x2 size, got "+ JSON.stringify(filter.fa); | |
| } | |
| fa=[]; | |
| for (let i=0; i<filter.fa.length; i++) { | |
| let c_str = filter.fa[i].toLowerCase(); | |
| let type=0; | |
| if (c_str=='r') type=4; | |
| else if (c_str=='g') type=5; | |
| else if (c_str=='b') type=6; | |
| else { | |
| throw "Only RGB component types supported in filter array"; | |
| } | |
| fa.push(type); | |
| } | |
| } | |
| pixel = []; | |
| for (let i=0; i<components.length; i++) { | |
| pixel.push(0); | |
| } | |
| pix_len = pixel.length; | |
| blocksize_bits = 8 * filter.block_size; | |
| if (!filter.vsize.x || !filter.vsize.y) | |
| throw "Image size shall not be 0"; | |
| if (!filter.tiles.x || !filter.tiles.y) | |
| throw "Image tiling shall not be 0"; | |
| if (filter.vsize.x % filter.tiles.x) | |
| throw "Image width not a multiple of num horizontal tiles"; | |
| if (filter.vsize.y % filter.tiles.y) | |
| throw "Image height not a multiple of num vertical tiles"; | |
| if (filter.interleave==INTERLEAVE_MULTIY) { | |
| let nb_y=0, nb_u=0, nb_v=0; | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| if (comp.type==1) nb_y++; | |
| if (comp.type==2) nb_u++; | |
| if (comp.type==3) nb_v++; | |
| } | |
| if ((nb_u!=1) && (nb_v!=1)) | |
| throw "Component pattern must have 1 U/Cb and 1 V/Cr"; | |
| if (filter.sampling==SAMPLING_422) { | |
| if (nb_y != 2) | |
| throw "Component pattern must have 2 Y, not " + nb_y; | |
| } | |
| else if (filter.sampling==SAMPLING_411) { | |
| if (nb_y != 4) | |
| throw "Component pattern must have 4 Y, not " + nb_y; | |
| } | |
| else { | |
| throw "Unsupported interleave type for sampling type " +filter.sampling; | |
| } | |
| if (components.length % 2) | |
| throw "Component pattern must have an even number of components"; | |
| } | |
| tile_width = filter.vsize.x / filter.tiles.x; | |
| tile_height = filter.vsize.y / filter.tiles.y; | |
| if (filter.sampling) { | |
| switch (filter.interleave) { | |
| case INTERLEAVE_COMPONENT: | |
| case INTERLEAVE_MIXED: | |
| case INTERLEAVE_MULTIY: | |
| break; | |
| default: | |
| if (filter.sampling==SAMPLING_420) | |
| throw "Sub-sampling must use interleave mode 0 or 2"; | |
| else | |
| throw "Sub-sampling must use interleave mode 0, 2 or 5"; | |
| break; | |
| } | |
| if (filter.sampling==SAMPLING_420) { | |
| if (tile_width%2) | |
| throw "Tile width must be a multiple of 2 for YUV 420 sampling"; | |
| if (tile_height%2) | |
| throw "Tile height must be a multiple of 2 for YUV 420 sampling"; | |
| } | |
| else if (filter.sampling==SAMPLING_422) { | |
| if (tile_width%2) | |
| throw "Tile width must be a multiple of 2 for YUV 422 sampling"; | |
| } | |
| else if (filter.sampling==SAMPLING_411) { | |
| if (tile_width%4) | |
| throw "Tile width must be a multiple of 4 for YUV 411 sampling"; | |
| } | |
| } | |
| let num_tiles = filter.tiles.x * filter.tiles.y; | |
| row_align_bytes = tile_align_bytes = 0; | |
| if ((filter.interleave==INTERLEAVE_TILE) && (num_tiles==1)) { | |
| throw "Tile-Component interleaving requires more than one tile"; | |
| } | |
| gen_yuv = false; | |
| let nb_y=0; | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| if (comp.type==1) { nb_y++; gen_yuv = true; } | |
| else if ((comp.type==2) || (comp.type==3)) gen_yuv = true; | |
| comp.row_align_size = 0; | |
| comp.tile_align_size = 0; | |
| } | |
| if ((nb_y>1) && (filter.interleave!=INTERLEAVE_MULTIY)) | |
| print(GF_LOG_WARNING, 'Multiple Y component but interleaving is not 5, undefined results'); | |
| if ((filter.interleave==INTERLEAVE_COMPONENT) || (filter.interleave==INTERLEAVE_ROW) || (filter.interleave==INTERLEAVE_TILE)) { | |
| out_size = 0; | |
| let tile_size=0; | |
| filter.write_pixel = writepix_interleave_0; | |
| //write component-interleave (planar), get the size of each component | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| comp.line_size = line_size([ {bits: comp.bits, align: comp.align} ] ); | |
| if ((comp.type==2) || (comp.type==3)) { | |
| if ((filter.sampling==SAMPLING_422) || (filter.sampling==SAMPLING_420)) | |
| comp.line_size /= 2; | |
| else if (filter.sampling==SAMPLING_411) | |
| comp.line_size /= 4; | |
| } | |
| if (filter.row_align) { | |
| while (comp.line_size % filter.row_align) { | |
| comp.line_size++; | |
| } | |
| comp.row_align_size = comp.line_size; | |
| } | |
| comp.plane_size = comp.line_size * tile_height; | |
| //for tile-component mode, tile align is on the plane size | |
| if (filter.tile_align && (filter.interleave==INTERLEAVE_TILE)) { | |
| while (comp.plane_size % filter.tile_align) { | |
| comp.plane_size++; | |
| } | |
| comp.tile_align_size = comp.plane_size; | |
| } | |
| if ((comp.type==2) || (comp.type==3)) { | |
| if (filter.sampling==SAMPLING_420) | |
| comp.plane_size /= 2; | |
| } | |
| tile_size += comp.plane_size; | |
| } | |
| //except for tile-component mode, tile align is on cumulated size of all components in tile | |
| if (filter.interleave!=INTERLEAVE_TILE) { | |
| if (filter.tile_align) { | |
| while (tile_size % filter.tile_align) { | |
| tile_size++; | |
| } | |
| tile_align_bytes = tile_size; | |
| } else { | |
| tile_align_bytes = tile_size; | |
| } | |
| } | |
| out_size += tile_size * num_tiles; | |
| if (gen_yuv) { | |
| if (filter.sampling==SAMPLING_420) | |
| filter.write_pixel = writepix_interleave_0_420; | |
| else if (filter.sampling==SAMPLING_422) | |
| filter.write_pixel = writepix_interleave_0_422; | |
| else if (filter.sampling==SAMPLING_411) | |
| filter.write_pixel = writepix_interleave_0_411; | |
| } | |
| } else if (filter.interleave==INTERLEAVE_MIXED) { | |
| out_size = 0; | |
| if (!filter.sampling || (filter.sampling>SAMPLING_411)) | |
| throw "bad sampling type for mixed interleave mode"; | |
| if (filter.row_align) | |
| throw "row align not supported in interleave=2 mode"; | |
| if (filter.tile_align) | |
| throw "tile align not supported in interleave=2 mode"; | |
| let uv_found=false; | |
| //write component-interleave (planar), get the size of each component | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| comp.line_size = line_size([ {bits: comp.bits, align: comp.align} ] ); | |
| if ((comp.type==2) || (comp.type==3)) { | |
| if (uv_found) { | |
| comp.line_size = 0; | |
| comp.plane_size = 0; | |
| continue; | |
| } | |
| uv_found = true; | |
| } | |
| comp.plane_size = comp.line_size * tile_height; | |
| if ((comp.type==2) || (comp.type==3)) { | |
| if (filter.sampling==SAMPLING_420) | |
| comp.plane_size /= 2; | |
| } | |
| out_size += comp.plane_size * num_tiles; | |
| } | |
| if (filter.sampling==SAMPLING_420) | |
| filter.write_pixel = writepix_interleave_2_420; | |
| else if (filter.sampling==SAMPLING_422) | |
| filter.write_pixel = writepix_interleave_2_422; | |
| else if (filter.sampling==SAMPLING_411) | |
| filter.write_pixel = writepix_interleave_2_411; | |
| } else if ((filter.interleave==INTERLEAVE_PIXEL) || (filter.interleave==INTERLEAVE_MULTIY)) { | |
| let comp_bits=[]; | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| comp_bits.push( {bits: comp.bits, align: comp.align} ); | |
| comp.line_size = 0; | |
| comp.plane_size = 0; | |
| } | |
| let lsize = line_size( comp_bits ); | |
| if (filter.pixel_size) { | |
| let lsize2 = tile_width * filter.pixel_size; | |
| if (lsize2<lsize) { | |
| throw "Invalid pixel size, smaller than required bits"; | |
| } | |
| lsize = lsize2; | |
| } | |
| if (filter.row_align) { | |
| while (lsize % filter.row_align) { | |
| lsize++; | |
| } | |
| row_align_bytes = lsize; | |
| } | |
| let psize = lsize * tile_height; | |
| tile_align_bytes = psize; | |
| if (filter.tile_align) { | |
| while (psize % filter.tile_align) { | |
| psize++; | |
| } | |
| tile_align_bytes = psize; | |
| } | |
| out_size = psize * num_tiles; | |
| filter.write_pixel = writepix_interleave_1; | |
| if (filter.interleave==INTERLEAVE_MULTIY) { | |
| if (filter.sampling==SAMPLING_422) subsample_x = 2; | |
| else if (filter.sampling==SAMPLING_411) subsample_x = 4; | |
| filter.write_pixel = writepix_interleave_multiy; | |
| } | |
| } else { | |
| throw "Unsupported interleave type"; | |
| } | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| let nbb = comp.bits; | |
| comp.max_val = 1; | |
| //palette is an index - for other types we normalize between 0 and 1 | |
| if (comp.type != 10) { | |
| while (nbb) { | |
| comp.max_val *= 2; | |
| nbb--; | |
| } | |
| comp.max_val--; | |
| } | |
| if (!min_bits || (min_bits>comp.bits)) | |
| min_bits = comp.bits; | |
| //map monochrome, Y or R to first comp | |
| if ((comp.type==0) || (comp.type==1) || (comp.type==4)) { | |
| if (r_y_idx<0) r_y_idx = i; | |
| comp.base_idx = r_y_idx; | |
| } | |
| else if ((comp.type==2) || (comp.type==5)) { | |
| if (g_u_idx<0) g_u_idx = i; | |
| comp.base_idx = g_u_idx; | |
| } | |
| else if ((comp.type==3) || (comp.type==6)) { | |
| if (b_v_idx<0) b_v_idx = i; | |
| comp.base_idx = b_v_idx; | |
| } | |
| //alpha | |
| else if (comp.type==7) { | |
| if (a_idx<0) a_idx = i; | |
| comp.base_idx = a_idx; | |
| } | |
| //depth or disp | |
| else if ((comp.type==8) || (comp.type==9)) { | |
| if (d_idx<0) d_idx = i; | |
| comp.base_idx = d_idx; | |
| } | |
| //palette | |
| else if (comp.type==10) { | |
| if (r_y_idx<0) r_y_idx = i; | |
| comp.base_idx = r_y_idx; | |
| } | |
| //filter array | |
| else if (comp.type==11) { | |
| if (r_y_idx<0) r_y_idx = i; | |
| comp.base_idx = r_y_idx; | |
| } | |
| else { | |
| comp.base_idx = -1; | |
| } | |
| } | |
| //print('Components setup: ' + JSON.stringify(components)); | |
| if (filter.cle && max_align_size) { | |
| comp_le_buf = new ArrayBuffer(max_align_size); | |
| comp_le_bs = new BS(comp_le_buf, true); | |
| comp_le_view = new Uint8Array(comp_le_buf); | |
| } | |
| } | |
| function get_pixel(i, j) | |
| { | |
| i -= cts * filter.vsize.x / max_cts; | |
| if (i<0) i += filter.vsize.x; | |
| if (filter.stereo && (i>=filter.vsize.x/2)) | |
| i-=filter.vsize.x/2; | |
| let r=0, g=0, b=0; | |
| let a=1; | |
| let d=0; | |
| if (tx) { | |
| let t_x = Math.floor(i * tx.width / filter.vsize.x); | |
| if (filter.stereo) t_x *= 2; | |
| let t_y = Math.floor(j * tx.height / filter.vsize.y); | |
| let tx_p = tx.get_pixel(t_x, t_y); | |
| r = tx_p.r; | |
| g = tx_p.g; | |
| b = tx_p.b; | |
| if (d_idx>=0) | |
| d = tx_p.a; | |
| else | |
| a = tx_p.a; | |
| } else { | |
| let tidx; | |
| if (filter.scpt) { | |
| let tile_x = Math.floor(i / tile_width); | |
| let tile_y = Math.floor(j / tile_height); | |
| tidx = (tile_x * filter.tiles.x + tile_y) % filter.pal.length; | |
| } else { | |
| tidx = Math.floor(i / col_width); | |
| if (filter.sq!=0) { | |
| let ridx = Math.floor(filter.sq*j / col_width); | |
| tidx += ridx; | |
| while (tidx>=filter.pal.length) | |
| tidx -= filter.pal.length; | |
| while (tidx<0) | |
| tidx += filter.pal.length; | |
| } | |
| } | |
| if (use_palette) { | |
| pixel[r_y_idx] = tidx; | |
| return; | |
| } | |
| let scale = 1 - j/filter.vsize.y; | |
| let col = filter.pal[tidx]; | |
| r = sys.color_component(col, 1); | |
| g = sys.color_component(col, 2); | |
| b = sys.color_component(col, 3); | |
| if (a_idx>=0) { | |
| if ((d_idx<0) && palette_alpha) | |
| a = sys.color_component(col, 0); | |
| else | |
| a = scale; | |
| } else if (filter.shade) { | |
| r *= scale; | |
| g *= scale; | |
| b *= scale; | |
| } | |
| if (d_idx>=0) { | |
| if (palette_alpha) | |
| d = sys.color_component(col, 0); | |
| else | |
| d = scale; | |
| } | |
| } | |
| if (gen_yuv) { | |
| let y = 0.299*r + 0.587 * g + 0.114 * b; | |
| let cb = -0.169*r - 0.331*g + 0.499*b + 0.5; | |
| let cr = 0.499*r - 0.418*g - 0.0813*b + 0.5; | |
| r = y; | |
| g = cb; | |
| b = cr; | |
| } | |
| else if (use_fa) { | |
| let p_x = i % fa_width; | |
| let p_y = j % fa_height; | |
| let type = fa[p_x + p_y*fa_height]; | |
| if (type==5) r = g; | |
| else if (type==6) r = b; | |
| } | |
| if (r_y_idx>=0) pixel[r_y_idx] = r; | |
| if (g_u_idx>=0) pixel[g_u_idx] = g; | |
| if (b_v_idx>=0) pixel[b_v_idx] = b; | |
| if (a_idx>=0) pixel[a_idx] = a; | |
| if (d_idx>=0) pixel[d_idx] = d; | |
| } | |
| //array of bitstream wrapper objects, one if pixel-interleave, one per comp if component-interleave (planar) | |
| let bs_array = []; | |
| function setup_block(bsw) | |
| { | |
| if (!blocksize_bits) return; | |
| bsw.next_block_idx = 0; | |
| bsw.nb_bits_block = 0; | |
| bsw.block_comps = []; | |
| if (filter.ble) { | |
| bsw.le_buf = new ArrayBuffer(filter.block_size); | |
| bsw.le_bs = new BS(bsw.le_buf, true); | |
| bsw.le_view = new Uint8Array(bsw.le_buf); | |
| } | |
| let max_comp = Math.ceil(blocksize_bits / min_bits); | |
| for (let i=0; i<max_comp; i++) { | |
| bsw.block_comps.push({val: 0, component: null}); | |
| } | |
| } | |
| let tile_start_offset = 0; | |
| function start_frame(data) | |
| { | |
| bs_array.length=0; | |
| tile_start_offset=0; | |
| //component interleave | |
| if ((filter.interleave==INTERLEAVE_PIXEL) || (filter.interleave==INTERLEAVE_MULTIY)) { | |
| let bsw = {}; | |
| bsw.bs = new BS(data, true); | |
| setup_block(bsw); | |
| bs_array.push(bsw); | |
| bsw.init_offset = 0; | |
| bsw.line_start_pos = 0; | |
| bsw.row_align_size = row_align_bytes; | |
| bsw.tile_align_size = 0; | |
| bsw.tile_size = tile_align_bytes; | |
| return; | |
| } | |
| //all other are planar modes | |
| let offset = 0; | |
| let tile_size=0; | |
| let comp_row_size=0; | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| let bsw = {}; | |
| bsw.bs = new BS(data, true); | |
| bsw.bs.pos = offset; | |
| bsw.init_offset = offset; | |
| bsw.tile_start_pos = offset; | |
| bsw.line_start_pos = offset; | |
| bsw.row_align_size = comp.row_align_size; | |
| bsw.tile_align_size = 0; | |
| setup_block(bsw); | |
| //row-interleave, first row of next comp starts after first row of this comp | |
| if (filter.interleave==INTERLEAVE_ROW) | |
| offset += comp.line_size; | |
| //tile-interleave, first row of next comp starts after last value of last tile of this comp | |
| else if (filter.interleave==INTERLEAVE_TILE) { | |
| offset += comp.plane_size * filter.tiles.x * filter.tiles.y; | |
| bsw.tile_align_size = comp.tile_align_size; | |
| } | |
| //otherwise next comp starts after last value for this comp | |
| else | |
| offset += comp.plane_size; | |
| comp_row_size += comp.line_size; | |
| tile_size += comp.plane_size; | |
| bsw.plane_size = comp.plane_size; | |
| bs_array.push(bsw); | |
| //tile size is per comp in this mode, not for the cumulated size | |
| if (filter.interleave==INTERLEAVE_TILE) | |
| bsw.tile_size = bsw.plane_size; | |
| } | |
| if (tile_align_bytes) | |
| tile_size = tile_align_bytes; | |
| bs_array.forEach(bsw => { | |
| bsw.comp_row_size = comp_row_size; | |
| if (filter.interleave!=INTERLEAVE_TILE) | |
| bsw.tile_size = tile_size; | |
| }); | |
| } | |
| function start_tile(tile_x, tile_y) | |
| { | |
| if ((filter.tiles.x==1) && (filter.tiles.y==1)) return; | |
| tile_start_offset = tile_align_bytes * (tile_x + filter.tiles.x * tile_y); | |
| bs_array.forEach(bsw => { | |
| let offs = tile_align_bytes ? tile_start_offset : (bsw.tile_size * (tile_x + filter.tiles.x * tile_y)); | |
| offs += bsw.init_offset; | |
| bsw.bs.pos = offs; | |
| bsw.tile_start_pos = offs; | |
| bsw.line_start_pos = offs; | |
| }); | |
| } | |
| function end_tile() | |
| { | |
| let max_pos=0; | |
| let max_bs=null; | |
| bs_array.forEach (bsw => { | |
| //per comp tile alignment, only in INTERLEAVE_TILE mode | |
| if (bsw.tile_align_size) { | |
| let remain = bsw.bs.pos - bsw.tile_start_pos; | |
| while (remain<bsw.tile_align_size) { | |
| bsw.bs.put_u8(0); | |
| remain++; | |
| } | |
| } | |
| if (max_pos<bsw.bs.pos) { | |
| max_pos = bsw.bs.pos; | |
| max_bs = bsw.bs; | |
| } | |
| }); | |
| if (tile_align_bytes) { | |
| let remain = max_pos - tile_start_offset; | |
| if (remain<0) throw "Internal error in tile padding: remainder is negative " + remain; | |
| if (remain>tile_align_bytes) throw "Internal error in tile padding: remainder " + remain + " is greater than tile size " + tile_align_bytes + ' tile start ' + tile_start_offset; | |
| while (remain<tile_align_bytes) { | |
| max_bs.put_u8(0); | |
| remain++; | |
| } | |
| } | |
| } | |
| function end_line(last_line_in_tile) | |
| { | |
| bs_array.forEach (bsw => { | |
| if (filter.block_size && bsw.next_block_idx) { | |
| flush_block(bsw); | |
| } | |
| //rows of tiles shall be byte-aligned at the end of the row | |
| bsw.bs.align(); | |
| if (bsw.row_align_size) { | |
| let remain = bsw.bs.pos - bsw.line_start_pos; | |
| while (remain<bsw.row_align_size) { | |
| bsw.bs.put_u8(0); | |
| remain++; | |
| } | |
| } | |
| //not last line of tile, update offsets | |
| //for last line we don't update, so that padding can be properly computed. We update the line offset at the beginning of next tile | |
| if (!last_line_in_tile) { | |
| //row interleave, seek to next row start | |
| if (filter.interleave==INTERLEAVE_ROW) { | |
| bsw.bs.pos = bsw.line_start_pos + bsw.comp_row_size; | |
| } | |
| bsw.line_start_pos = bsw.bs.pos; | |
| } | |
| }); | |
| } | |
| function write_val(bs, val, comp) | |
| { | |
| if (comp.align) bs.align(); | |
| if (comp.mode==0) { | |
| if (comp.align) { | |
| if (comp_le_buf) { | |
| comp_le_bs.pos = 0; | |
| comp_le_bs.put_bits(val * comp.max_val, 8*comp.align); | |
| for (let i=comp.align; i>0; i--) { | |
| bs.put_u8(comp_le_view[i-1]); | |
| } | |
| } else { | |
| bs.put_bits(val * comp.max_val, 8*comp.align); | |
| } | |
| } | |
| else | |
| bs.put_bits(val * comp.max_val, comp.bits); | |
| } | |
| else if (comp.mode==1) { | |
| if (comp.bits==32) | |
| bs.put_float(val); | |
| else if (comp.bits==64) | |
| bs.put_double(val); | |
| } | |
| } | |
| function flush_block(bsw) | |
| { | |
| let i, bits=0; | |
| for (i=0; i<bsw.next_block_idx; i++) { | |
| let comp = bsw.block_comps[i].component; | |
| if (!comp) break; | |
| if (comp.align) { | |
| while (bits % 8) bits++; | |
| bits += comp.align*8; | |
| } else { | |
| bits += comp.bits; | |
| } | |
| } | |
| let pad = blocksize_bits - bits; | |
| if (pad<0) | |
| throw "Wrong block size " + blocksize_bits + " vs " + bits + " nb comps " + bsw.next_block_idx; | |
| if (filter.ble) { | |
| let bs_le = bsw.le_bs; | |
| bs_le.pos = 0; | |
| if (!filter.pad_lsb) | |
| bs_le.put_bits(0, pad); | |
| for (i=0; i<bsw.next_block_idx; i++) { | |
| //check block reverse | |
| let b_idx = filter.br ? (bsw.next_block_idx - i - 1) : i; | |
| let cval = bsw.block_comps[b_idx]; | |
| write_val(bs_le, cval.val, cval.component); | |
| cval.component = null; | |
| } | |
| if (filter.pad_lsb) | |
| bs_le.put_bits(0, pad); | |
| for (i=filter.block_size; i>0; i--) { | |
| bsw.bs.put_u8(bsw.le_view[i-1]); | |
| } | |
| } else { | |
| if (!filter.pad_lsb) | |
| bsw.bs.put_bits(0, pad); | |
| //big endian, no block reverse | |
| for (i=0; i<bsw.next_block_idx; i++) { | |
| let cval = bsw.block_comps[i]; | |
| if (!cval.component) break; | |
| write_val(bsw.bs, cval.val, cval.component); | |
| cval.component = null; | |
| } | |
| if (filter.pad_lsb) | |
| bsw.bs.put_bits(0, pad); | |
| } | |
| bsw.nb_bits_block=0; | |
| bsw.next_block_idx = 0; | |
| } | |
| function push_val(bsw, c, comp) | |
| { | |
| if (filter.block_size) { | |
| if (comp.align) { | |
| while (bsw.nb_bits_block % 8) | |
| bsw.nb_bits_block++; | |
| } | |
| let cbits = comp.align ? (comp.align*8) : comp.bits; | |
| if (bsw.nb_bits_block + cbits > blocksize_bits) { | |
| flush_block(bsw); | |
| } | |
| bsw.block_comps[bsw.next_block_idx].val = c; | |
| bsw.block_comps[bsw.next_block_idx].component = comp; | |
| bsw.next_block_idx++; | |
| bsw.nb_bits_block += cbits; | |
| } else { | |
| write_val(bsw.bs, c, comp); | |
| } | |
| } | |
| function writepix_interleave_0(x, y) | |
| { | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| let c = (pix_len>i) ? pixel[i] : 0; | |
| push_val(bs_array[i], c, comp); | |
| } | |
| } | |
| function writepix_interleave_0_420(x, y) | |
| { | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| let c = (pix_len>i) ? pixel[i] : 0; | |
| if ((comp.type==2) || (comp.type==3)) { | |
| if (x%2) continue; | |
| if (y%2) continue; | |
| } | |
| push_val(bs_array[i], c, comp); | |
| } | |
| } | |
| function writepix_interleave_0_422(x, y) | |
| { | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| let c = (pix_len>i) ? pixel[i] : 0; | |
| if ((comp.type==2) || (comp.type==3)) { | |
| if (x%2) continue; | |
| } | |
| push_val(bs_array[i], c, comp); | |
| } | |
| } | |
| function writepix_interleave_0_411(x, y) | |
| { | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| let c = (pix_len>i) ? pixel[i] : 0; | |
| if ((comp.type==2) || (comp.type==3)) { | |
| if (x%4) continue; | |
| } | |
| push_val(bs_array[i], c, comp); | |
| } | |
| } | |
| function writepix_interleave_1(x, y) | |
| { | |
| let bsw = bs_array[0]; | |
| let psize = bsw.bs.pos; | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| let c = (pix_len>i) ? pixel[i] : 0; | |
| push_val(bsw, c, comp); | |
| } | |
| if (filter.pixel_size) { | |
| if (filter.block_size) { | |
| if (bsw.next_block_idx) { | |
| flush_block(bsw); | |
| } | |
| } | |
| else | |
| bsw.bs.align(); | |
| psize = bsw.bs.pos - psize; | |
| if (psize>filter.pixel_size) { | |
| throw "Invalid pixel_size " + filter.pixel_size + ", less than total size of components " + psize; | |
| } | |
| while (psize<filter.pixel_size) { | |
| bsw.bs.put_u8(0); | |
| psize++; | |
| } | |
| } | |
| } | |
| function writepix_interleave_2_420(x, y) | |
| { | |
| let bs_uv=null; | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| let c = (pix_len>i) ? pixel[i] : 0; | |
| let bs = bs_array[i]; | |
| if ((comp.type==2) || (comp.type==3)) { | |
| if (x%2) continue; | |
| if (y%2) continue; | |
| if (!bs_uv) bs_uv = bs; | |
| bs = bs_uv; | |
| } | |
| push_val(bs, c, comp); | |
| } | |
| } | |
| function writepix_interleave_2_422(x, y) | |
| { | |
| let bs_uv=null; | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| let c = (pix_len>i) ? pixel[i] : 0; | |
| let bs = bs_array[i]; | |
| if ((comp.type==2) || (comp.type==3)) { | |
| if (x%2) continue; | |
| if (!bs_uv) bs_uv = bs; | |
| bs = bs_uv; | |
| } | |
| push_val(bs, c, comp); | |
| } | |
| } | |
| function writepix_interleave_2_411(x, y) | |
| { | |
| let bs_uv=null; | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| let c = (pix_len>i) ? pixel[i] : 0; | |
| let bs = bs_array[i]; | |
| if ((comp.type==2) || (comp.type==3)) { | |
| if (x%4) continue; | |
| if (!bs_uv) bs_uv = bs; | |
| bs = bs_uv; | |
| } | |
| push_val(bs, c, comp); | |
| } | |
| } | |
| function writepix_interleave_multiy(x, y) | |
| { | |
| if (! (x % subsample_x)) { | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| comp.stored = false; | |
| if (comp.type==2) comp.store_val = pixel[g_u_idx]; | |
| else if (comp.type==3) comp.store_val = pixel[b_v_idx]; | |
| } | |
| } | |
| let types=[]; | |
| let bsw = bs_array[0]; | |
| let psize = bsw.bs.pos; | |
| for (let i=0; i<components.length; i++) { | |
| let comp = components[i]; | |
| if (comp.stored) continue; | |
| //we already wrote a component of that type, we need next pixel | |
| if (types.includes(comp.type) ) break; | |
| let c; | |
| if ((comp.type==2) || (comp.type==3)) { | |
| c = comp.store_val; | |
| } else if (comp.base_idx>=0) { | |
| c = pixel[comp.base_idx]; | |
| } else { | |
| c = 0; | |
| } | |
| push_val(bsw, c, comp); | |
| comp.stored = true; | |
| types.push(comp.type); | |
| } | |
| if (filter.pixel_size) { | |
| if (filter.block_size) { | |
| if (bsw.next_block_idx) | |
| flush_block(bws) | |
| } | |
| else | |
| bsw.bs.align(); | |
| psize = bsw.bs.pos - psize; | |
| if (psize>filter.pixel_size) { | |
| throw "Invalid pixel_size " + filter.pixel_size + ", less than total size of components " + psize; | |
| } | |
| while (psize<filter.pixel_size) { | |
| bsw.bs.put_u8(0); | |
| psize++; | |
| } | |
| } | |
| } | |
| filter.process = function() | |
| { | |
| if (!is_playing) | |
| return GF_EOS; | |
| if (cts >= max_cts) { | |
| return GF_EOS; | |
| } | |
| // try { | |
| let pck = pid.new_packet(out_size); | |
| start_frame(pck.data); | |
| for (let t_j=0; t_j<filter.tiles.y; t_j++) { | |
| for (let t_i=0; t_i<filter.tiles.x; t_i++) { | |
| let t_y = t_j * tile_height; | |
| let t_x = t_i * tile_width; | |
| start_tile(t_i, t_j); | |
| for (let j=0; j<tile_height; j++) { | |
| for (let i=0; i<tile_width; i++) { | |
| get_pixel(t_x + i, t_y + j); | |
| this.write_pixel(t_x + i, t_y + j); | |
| } | |
| end_line((j+1==tile_height) ? true : false); | |
| } | |
| end_tile(); | |
| } | |
| } | |
| /*set packet properties and send it*/ | |
| pck.cts = cts; | |
| pck.sap = GF_FILTER_SAP_1; | |
| pck.send(); | |
| cts ++; | |
| if (cts == max_cts) { | |
| pid.eos = true; | |
| return GF_EOS; | |
| } | |
| print('Frame done ' + cts + '/' + max_cts + '\r'); | |
| return GF_OK; | |
| /* } catch (e) { | |
| print('Error writing frame ' + e); | |
| is_playing=false; | |
| return GF_EOS; | |
| } | |
| */ | |
| } | |
| filter.process_event = function(pid, evt) | |
| { | |
| if (evt.type == GF_FEVT_STOP) { | |
| is_playing = false; | |
| } | |
| else if (evt.type == GF_FEVT_PLAY) { | |
| is_playing = true; | |
| } | |
| } | |