filters/text-overlay.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al. | ||
| 2 | // http://avisynth.nl | ||
| 3 | |||
| 4 | // This program is free software; you can redistribute it and/or modify | ||
| 5 | // it under the terms of the GNU General Public License as published by | ||
| 6 | // the Free Software Foundation; either version 2 of the License, or | ||
| 7 | // (at your option) any later version. | ||
| 8 | // | ||
| 9 | // This program is distributed in the hope that it will be useful, | ||
| 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | // GNU General Public License for more details. | ||
| 13 | // | ||
| 14 | // You should have received a copy of the GNU General Public License | ||
| 15 | // along with this program; if not, write to the Free Software | ||
| 16 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit | ||
| 17 | // http://www.gnu.org/copyleft/gpl.html . | ||
| 18 | // | ||
| 19 | // Linking Avisynth statically or dynamically with other modules is making a | ||
| 20 | // combined work based on Avisynth. Thus, the terms and conditions of the GNU | ||
| 21 | // General Public License cover the whole combination. | ||
| 22 | // | ||
| 23 | // As a special exception, the copyright holders of Avisynth give you | ||
| 24 | // permission to link Avisynth with independent modules that communicate with | ||
| 25 | // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license | ||
| 26 | // terms of these independent modules, and to copy and distribute the | ||
| 27 | // resulting combined work under terms of your choice, provided that | ||
| 28 | // every copy of the combined work is accompanied by a complete copy of | ||
| 29 | // the source code of Avisynth (the version of Avisynth used to produce the | ||
| 30 | // combined work), being distributed under the terms of the GNU General | ||
| 31 | // Public License plus this exception. An independent module is a module | ||
| 32 | // which is not derived from or based on Avisynth, such as 3rd-party filters, | ||
| 33 | // import and export plugins, or graphical user interfaces. | ||
| 34 | |||
| 35 | #include <avs/config.h> | ||
| 36 | #ifdef AVS_WINDOWS | ||
| 37 | #include <avs/win.h> | ||
| 38 | #else | ||
| 39 | #include <avs/posix.h> | ||
| 40 | #endif | ||
| 41 | |||
| 42 | #include "text-overlay.h" | ||
| 43 | #include "getalpharect_impl.h" | ||
| 44 | #include "getalpharect_scalar.h" | ||
| 45 | #ifdef INTEL_INTRINSICS | ||
| 46 | #include "intel/text-overlay_sse.h" | ||
| 47 | #ifdef INTEL_INTRINSICS_AVX512 | ||
| 48 | #include "intel/getalpharect_avx512.h" | ||
| 49 | #endif | ||
| 50 | #include "overlay/intel/masked_rowprep_sse41.h" | ||
| 51 | #include "overlay/intel/masked_rowprep_avx2.h" | ||
| 52 | #endif | ||
| 53 | #include "../convert/convert_matrix.h" // for RGB2YUV_Rec601 | ||
| 54 | #include "../convert/convert_helper.h" // chroma location | ||
| 55 | |||
| 56 | #define __STDC_FORMAT_MACROS | ||
| 57 | #include <inttypes.h> | ||
| 58 | #include <sstream> | ||
| 59 | #include <cstdint> | ||
| 60 | #include <cmath> | ||
| 61 | #include <algorithm> | ||
| 62 | #include <avs/minmax.h> | ||
| 63 | #include "../core/internal.h" | ||
| 64 | #include "../core/info.h" | ||
| 65 | #include "../core/strings.h" | ||
| 66 | #include "../core/audio.h" | ||
| 67 | #include <bitset> | ||
| 68 | |||
| 69 | |||
| 70 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 71 | static HFONT LoadFont(const char name[], int size, bool bold, bool italic, int width=0, int angle=0) | ||
| 72 | { | ||
| 73 | return CreateFont( size, width, angle, angle, bold ? FW_BOLD : FW_NORMAL, | ||
| 74 | italic, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, | ||
| 75 | CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE | FIXED_PITCH /*FF_DONTCARE | DEFAULT_PITCH*/, name ); | ||
| 76 | // avs+: force fixed pitch when font is not found by name | ||
| 77 | } | ||
| 78 | #endif | ||
| 79 | |||
| 80 | /******************************************************************** | ||
| 81 | ***** Declare index of new filters for Avisynth's filter engine ***** | ||
| 82 | ********************************************************************/ | ||
| 83 | |||
| 84 | extern const AVSFunction Text_filters[] = { | ||
| 85 | { "ShowFrameNumber",BUILTIN_FUNC_PREFIX, | ||
| 86 | "c[scroll]b[offset]i[x]f[y]f[font]s[size]f[text_color]i[halo_color]i[font_width]f[font_angle]f[bold]b[italic]b[noaa]b[gdi]b", | ||
| 87 | ShowFrameNumber::Create }, | ||
| 88 | |||
| 89 | { "ShowCRC32",BUILTIN_FUNC_PREFIX, | ||
| 90 | "c[scroll]b[offset]i[x]f[y]f[font]s[size]f[text_color]i[halo_color]i[font_width]f[font_angle]f[bold]b[italic]b[noaa]b[channels]s[mode]i[showmode]i[gdi]b", | ||
| 91 | ShowCRC32::Create }, | ||
| 92 | |||
| 93 | { "ShowSMPTE",BUILTIN_FUNC_PREFIX, | ||
| 94 | "c[fps]f[offset]s[offset_f]i[x]f[y]f[font]s[size]f[text_color]i[halo_color]i[font_width]f[font_angle]f[bold]b[italic]b[noaa]b[gdi]b", | ||
| 95 | ShowSMPTE::CreateSMTPE }, | ||
| 96 | |||
| 97 | { "ShowTime",BUILTIN_FUNC_PREFIX, | ||
| 98 | "c[offset_f]i[x]f[y]f[font]s[size]f[text_color]i[halo_color]i[font_width]f[font_angle]f[bold]b[italic]b[noaa]b[gdi]b", | ||
| 99 | ShowSMPTE::CreateTime }, | ||
| 100 | |||
| 101 | { "Info", BUILTIN_FUNC_PREFIX, "c[font]s[size]f[text_color]i[halo_color]i[bold]b[italic]b[noaa]b[cpu]b[x]f[y]f[align]i[gdi]b", FilterInfo::Create }, // clip | ||
| 102 | |||
| 103 | { "Subtitle",BUILTIN_FUNC_PREFIX, | ||
| 104 | "cs[x]f[y]f[first_frame]i[last_frame]i[font]s[size]f[text_color]i[halo_color]i" | ||
| 105 | "[align]i[spc]i[lsp]i[font_width]f[font_angle]f[interlaced]b[font_filename]s[utf8]b[bold]b[italic]b[noaa]b[placement]s[gdi]b", | ||
| 106 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 107 | Subtitle::Create | ||
| 108 | #else | ||
| 109 | SimpleText::Create // poor man's SubTitle, it's simulated with SimpleText | ||
| 110 | #endif | ||
| 111 | }, // see docs! | ||
| 112 | |||
| 113 | { "Compare",BUILTIN_FUNC_PREFIX, | ||
| 114 | "cc[channels]s[logfile]s[show_graph]b[gdi]b", | ||
| 115 | Compare::Create }, | ||
| 116 | |||
| 117 | { "Text",BUILTIN_FUNC_PREFIX, | ||
| 118 | "cs[x]f[y]f[first_frame]i[last_frame]i[font]s[size]f[text_color]i[halo_color]i" | ||
| 119 | "[align]i[spc]i[lsp]i[font_width]f[font_angle]f[interlaced]b[font_filename]s[utf8]b[bold]b[italic]b[noaa]b[placement]s[gdi]b", | ||
| 120 | SimpleText::Create }, | ||
| 121 | |||
| 122 | { 0 } | ||
| 123 | }; | ||
| 124 | |||
| 125 | |||
| 126 | |||
| 127 | |||
| 128 | |||
| 129 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 130 | /****************************** | ||
| 131 | ******* Anti-alias ****** | ||
| 132 | *****************************/ | ||
| 133 | |||
| 134 | // Select the best rowprep function for soa_mask_mode at construction time. | ||
| 135 | using rowprep_u16_fn_t = const uint16_t*(*)(const uint16_t*, int, int, std::vector<uint16_t>&, int, int, MagicDiv); | ||
| 136 | |||
| 137 | static rowprep_u16_fn_t select_rowprep_u16(MaskMode mode, int64_t cpuFlags) | ||
| 138 | { | ||
| 139 | #ifdef INTEL_INTRINSICS | ||
| 140 | if (cpuFlags & CPUF_AVX2) { | ||
| 141 | switch (mode) { | ||
| 142 | case MASK444: return prepare_effective_mask_for_row_avx2<MASK444, uint16_t, true>; | ||
| 143 | case MASK411: return prepare_effective_mask_for_row_avx2<MASK411, uint16_t, true>; | ||
| 144 | case MASK420: return prepare_effective_mask_for_row_avx2<MASK420, uint16_t, true>; | ||
| 145 | case MASK420_MPEG2: return prepare_effective_mask_for_row_avx2<MASK420_MPEG2, uint16_t, true>; | ||
| 146 | case MASK420_TOPLEFT: return prepare_effective_mask_for_row_avx2<MASK420_TOPLEFT, uint16_t, true>; | ||
| 147 | case MASK422: return prepare_effective_mask_for_row_avx2<MASK422, uint16_t, true>; | ||
| 148 | case MASK422_MPEG2: return prepare_effective_mask_for_row_avx2<MASK422_MPEG2, uint16_t, true>; | ||
| 149 | case MASK422_TOPLEFT: return prepare_effective_mask_for_row_avx2<MASK422_TOPLEFT, uint16_t, true>; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | if (cpuFlags & CPUF_SSE4_1) { | ||
| 153 | switch (mode) { | ||
| 154 | case MASK444: return prepare_effective_mask_for_row_sse41<MASK444, uint16_t, true>; | ||
| 155 | case MASK411: return prepare_effective_mask_for_row_sse41<MASK411, uint16_t, true>; | ||
| 156 | case MASK420: return prepare_effective_mask_for_row_sse41<MASK420, uint16_t, true>; | ||
| 157 | case MASK420_MPEG2: return prepare_effective_mask_for_row_sse41<MASK420_MPEG2, uint16_t, true>; | ||
| 158 | case MASK420_TOPLEFT: return prepare_effective_mask_for_row_sse41<MASK420_TOPLEFT, uint16_t, true>; | ||
| 159 | case MASK422: return prepare_effective_mask_for_row_sse41<MASK422, uint16_t, true>; | ||
| 160 | case MASK422_MPEG2: return prepare_effective_mask_for_row_sse41<MASK422_MPEG2, uint16_t, true>; | ||
| 161 | case MASK422_TOPLEFT: return prepare_effective_mask_for_row_sse41<MASK422_TOPLEFT, uint16_t, true>; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | #else | ||
| 165 | (void)cpuFlags; | ||
| 166 | #endif | ||
| 167 | switch (mode) { | ||
| 168 | case MASK444: return prepare_effective_mask_for_row<MASK444, uint16_t, true>; | ||
| 169 | case MASK411: return prepare_effective_mask_for_row<MASK411, uint16_t, true>; | ||
| 170 | case MASK420: return prepare_effective_mask_for_row<MASK420, uint16_t, true>; | ||
| 171 | case MASK420_MPEG2: return prepare_effective_mask_for_row<MASK420_MPEG2, uint16_t, true>; | ||
| 172 | case MASK420_TOPLEFT: return prepare_effective_mask_for_row<MASK420_TOPLEFT, uint16_t, true>; | ||
| 173 | case MASK422: return prepare_effective_mask_for_row<MASK422, uint16_t, true>; | ||
| 174 | case MASK422_MPEG2: return prepare_effective_mask_for_row<MASK422_MPEG2, uint16_t, true>; | ||
| 175 | case MASK422_TOPLEFT: return prepare_effective_mask_for_row<MASK422_TOPLEFT, uint16_t, true>; | ||
| 176 | } | ||
| 177 | return nullptr; | ||
| 178 | } | ||
| 179 | |||
| 180 | Antialiaser::Antialiaser(int width, int height, const char fontname[], int size, | ||
| 181 | int _textcolor, int _halocolor, bool _bold, bool _italic, bool _noaa, | ||
| 182 | int64_t cpuFlags, | ||
| 183 | int _chromaplacement, | ||
| 184 | int font_width, int font_angle, bool _interlaced) : | ||
| 185 | soa_buf(nullptr), w_stride(0), | ||
| 186 | chromaplacement(_chromaplacement), | ||
| 187 | w(width), h(height), textcolor(_textcolor), halocolor(_halocolor), | ||
| 188 | dirty(true), interlaced(_interlaced), | ||
| 189 | bold(_bold), italic(_italic), noaa(_noaa) | ||
| 190 | { | ||
| 191 | // row preparation functions convert a luma mask to chroma masks with correct chroma placement | ||
| 192 | // for subsampled formats. | ||
| 193 | // Pre-select SIMD rowprep for all 8 MaskModes (411, 420 and 422 variants, 444) | ||
| 194 | for (int m = 0; m < 8; ++m) | ||
| 195 | rowprep_fns[m] = select_rowprep_u16(static_cast<MaskMode>(m), cpuFlags); | ||
| 196 | |||
| 197 | #ifdef INTEL_INTRINSICS | ||
| 198 | // Select best GetAlphaRect implementation (noaa and interlaced baked in via template) | ||
| 199 | getalpharect_fn = nullptr; | ||
| 200 | #ifdef INTEL_INTRINSICS_AVX512 | ||
| 201 | if (cpuFlags & CPUF_AVX512_FAST) | ||
| 202 | getalpharect_fn = GetAlphaRect_select_avx512(_noaa, _interlaced); | ||
| 203 | #endif | ||
| 204 | if (!getalpharect_fn && (cpuFlags & CPUF_AVX2)) | ||
| 205 | getalpharect_fn = GetAlphaRect_select_avx2(_noaa, _interlaced); | ||
| 206 | if (!getalpharect_fn && (cpuFlags & CPUF_SSE4_1)) | ||
| 207 | getalpharect_fn = GetAlphaRect_select_sse41(_noaa, _interlaced); | ||
| 208 | // nullptr means scalar fallback (called directly inside GetAlphaRect()) | ||
| 209 | #endif | ||
| 210 | |||
| 211 | struct { | ||
| 212 | BITMAPINFOHEADER bih; | ||
| 213 | RGBQUAD clr[2]; | ||
| 214 | } b; | ||
| 215 | |||
| 216 | b.bih.biSize = sizeof(BITMAPINFOHEADER); | ||
| 217 | b.bih.biWidth = width * 8 + 32; | ||
| 218 | b.bih.biHeight = height * 8 + 32; | ||
| 219 | b.bih.biBitCount = 1; | ||
| 220 | b.bih.biPlanes = 1; | ||
| 221 | b.bih.biCompression = BI_RGB; | ||
| 222 | b.bih.biXPelsPerMeter = 0; | ||
| 223 | b.bih.biYPelsPerMeter = 0; | ||
| 224 | b.bih.biClrUsed = 2; | ||
| 225 | b.bih.biClrImportant = 2; | ||
| 226 | b.clr[0].rgbBlue = b.clr[0].rgbGreen = b.clr[0].rgbRed = 0; | ||
| 227 | b.clr[1].rgbBlue = b.clr[1].rgbGreen = b.clr[1].rgbRed = 255; | ||
| 228 | |||
| 229 | hdcAntialias = CreateCompatibleDC(NULL); | ||
| 230 | if (hdcAntialias) { | ||
| 231 | hbmAntialias = CreateDIBSection | ||
| 232 | ( hdcAntialias, | ||
| 233 | (BITMAPINFO *)&b, | ||
| 234 | DIB_RGB_COLORS, | ||
| 235 | &lpAntialiasBits, | ||
| 236 | NULL, | ||
| 237 | 0 ); | ||
| 238 | if (hbmAntialias) { | ||
| 239 | hbmDefault = (HBITMAP)SelectObject(hdcAntialias, hbmAntialias); | ||
| 240 | HFONT newfont = LoadFont(fontname, size, bold, italic, font_width, font_angle); | ||
| 241 | hfontDefault = newfont ? (HFONT)SelectObject(hdcAntialias, newfont) : 0; | ||
| 242 | |||
| 243 | SetMapMode(hdcAntialias, MM_TEXT); | ||
| 244 | SetTextColor(hdcAntialias, 0xffffff); | ||
| 245 | SetBkColor(hdcAntialias, 0); | ||
| 246 | |||
| 247 | // 64 byte boundary of the four planes for row-interleaved SoA | ||
| 248 | // One single buffer allocation for multiple planes, | ||
| 249 | w_stride = (width + 31) & ~31; | ||
| 250 | soa_buf = new(std::nothrow) uint16_t[w_stride * height * 4]; | ||
| 251 | if (!soa_buf) { | ||
| 252 | FreeDC(); | ||
| 253 | } else { | ||
| 254 | uv_buf_ba.resize(width); | ||
| 255 | uv_buf_u.resize(width); | ||
| 256 | uv_buf_v.resize(width); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | |||
| 263 | Antialiaser::~Antialiaser() { | ||
| 264 | FreeDC(); | ||
| 265 | delete[] soa_buf; | ||
| 266 | } | ||
| 267 | |||
| 268 | |||
| 269 | HDC Antialiaser::GetDC() { | ||
| 270 | dirty = true; | ||
| 271 | return hdcAntialias; | ||
| 272 | } | ||
| 273 | |||
| 274 | |||
| 275 | void Antialiaser::FreeDC() { | ||
| 276 | if (hdcAntialias) { // :FIXME: Interlocked | ||
| 277 | if (hbmDefault) { | ||
| 278 | DeleteObject(SelectObject(hdcAntialias, hbmDefault)); | ||
| 279 | hbmDefault = 0; | ||
| 280 | } | ||
| 281 | if (hfontDefault) { | ||
| 282 | DeleteObject(SelectObject(hdcAntialias, hfontDefault)); | ||
| 283 | hfontDefault = 0; | ||
| 284 | } | ||
| 285 | DeleteDC(hdcAntialias); | ||
| 286 | hdcAntialias = 0; | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | |||
| 291 | void Antialiaser::Apply(const VideoInfo& vi, PVideoFrame* frame, int pitch) | ||
| 292 | { | ||
| 293 | if (!soa_buf) return; | ||
| 294 | |||
| 295 | if (vi.IsRGB32()) | ||
| 296 | ApplyRGB_packed<uint8_t, true>((*frame)->GetWritePtr(), pitch); | ||
| 297 | else if (vi.IsRGB64()) | ||
| 298 | ApplyRGB_packed<uint16_t, true>((*frame)->GetWritePtr(), pitch); | ||
| 299 | else if (vi.IsRGB24()) | ||
| 300 | ApplyRGB_packed<uint8_t, false>((*frame)->GetWritePtr(), pitch); | ||
| 301 | else if (vi.IsRGB48()) | ||
| 302 | ApplyRGB_packed<uint16_t, false>((*frame)->GetWritePtr(), pitch); | ||
| 303 | else if (vi.IsYUY2()) | ||
| 304 | ApplyYUY2((*frame)->GetWritePtr(), pitch); | ||
| 305 | else if (vi.IsPlanar()) { | ||
| 306 | const bool isRGB = vi.IsPlanarRGB() || vi.IsPlanarRGBA(); | ||
| 307 | BYTE* bufY = isRGB ? (*frame)->GetWritePtr(PLANAR_R) : (*frame)->GetWritePtr(); | ||
| 308 | BYTE* bufU = nullptr; | ||
| 309 | BYTE* bufV = nullptr; | ||
| 310 | int pitchUV = 0; | ||
| 311 | if (vi.NumComponents() > 1) { | ||
| 312 | pitchUV = isRGB ? (*frame)->GetPitch(PLANAR_G) : (*frame)->GetPitch(PLANAR_U); | ||
| 313 | bufU = isRGB ? (*frame)->GetWritePtr(PLANAR_G) : (*frame)->GetWritePtr(PLANAR_U); | ||
| 314 | bufV = isRGB ? (*frame)->GetWritePtr(PLANAR_B) : (*frame)->GetWritePtr(PLANAR_V); | ||
| 315 | } | ||
| 316 | const int bpp = vi.BitsPerComponent(); | ||
| 317 | #define CALL_SOA(mode) \ | ||
| 318 | switch (bpp) { \ | ||
| 319 | case 8: ApplyPlanar_SoA<mode, 8>(bufY, pitch, pitchUV, bufU, bufV, isRGB); break; \ | ||
| 320 | case 10: ApplyPlanar_SoA<mode, 10>(bufY, pitch, pitchUV, bufU, bufV, isRGB); break; \ | ||
| 321 | case 12: ApplyPlanar_SoA<mode, 12>(bufY, pitch, pitchUV, bufU, bufV, isRGB); break; \ | ||
| 322 | case 14: ApplyPlanar_SoA<mode, 14>(bufY, pitch, pitchUV, bufU, bufV, isRGB); break; \ | ||
| 323 | case 16: ApplyPlanar_SoA<mode, 16>(bufY, pitch, pitchUV, bufU, bufV, isRGB); break; \ | ||
| 324 | case 32: ApplyPlanar_SoA<mode, 32>(bufY, pitch, pitchUV, bufU, bufV, isRGB); break; \ | ||
| 325 | } | ||
| 326 | // Compute MaskMode from clip subsampling + stored chromaplacement | ||
| 327 | MaskMode mode = MASK444; | ||
| 328 | if ((vi.IsYUV() || vi.IsYUVA()) && !vi.IsY()) { | ||
| 329 | const int sx = vi.GetPlaneWidthSubsampling(PLANAR_U); | ||
| 330 | const int sy = vi.GetPlaneHeightSubsampling(PLANAR_U); | ||
| 331 | if (sx == 2) { | ||
| 332 | mode = MASK411; // always center averaging | ||
| 333 | } else if (sx == 1 && sy == 1) { | ||
| 334 | switch (chromaplacement) { | ||
| 335 | case ChromaLocation_e::AVS_CHROMA_LEFT: mode = MASK420_MPEG2; break; | ||
| 336 | case ChromaLocation_e::AVS_CHROMA_TOP_LEFT: mode = MASK420_TOPLEFT; break; | ||
| 337 | default: mode = MASK420; break; // center | ||
| 338 | } | ||
| 339 | } else if (sx == 1 && sy == 0) { | ||
| 340 | switch (chromaplacement) { | ||
| 341 | case ChromaLocation_e::AVS_CHROMA_LEFT: mode = MASK422_MPEG2; break; | ||
| 342 | case ChromaLocation_e::AVS_CHROMA_TOP_LEFT: mode = MASK422_TOPLEFT; break; | ||
| 343 | default: mode = MASK422; break; // center | ||
| 344 | } | ||
| 345 | } | ||
| 346 | } | ||
| 347 | switch (mode) { | ||
| 348 | case MASK444: CALL_SOA(MASK444); break; | ||
| 349 | case MASK411: CALL_SOA(MASK411); break; | ||
| 350 | case MASK420: CALL_SOA(MASK420); break; | ||
| 351 | case MASK420_MPEG2: CALL_SOA(MASK420_MPEG2); break; | ||
| 352 | case MASK420_TOPLEFT: CALL_SOA(MASK420_TOPLEFT); break; | ||
| 353 | case MASK422: CALL_SOA(MASK422); break; | ||
| 354 | case MASK422_MPEG2: CALL_SOA(MASK422_MPEG2); break; | ||
| 355 | case MASK422_TOPLEFT: CALL_SOA(MASK422_TOPLEFT); break; | ||
| 356 | } | ||
| 357 | #undef CALL_SOA | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | // Hoping that bits_per_pixel template make it quicker | ||
| 362 | template<MaskMode maskMode, int bits_per_pixel> | ||
| 363 | void Antialiaser::ApplyPlanar_SoA(BYTE* buf, int pitch, int pitchUV, BYTE* bufU, BYTE* bufV, bool isRGB) | ||
| 364 | { | ||
| 365 | constexpr int shiftX = | ||
| 366 | (maskMode == MASK444) ? 0 : | ||
| 367 | (maskMode == MASK411) ? 2 : 1; | ||
| 368 | constexpr int shiftY = | ||
| 369 | (maskMode == MASK420 || maskMode == MASK420_MPEG2 || maskMode == MASK420_TOPLEFT) ? 1 : 0; | ||
| 370 | constexpr int stepX = 1 << shiftX; | ||
| 371 | constexpr int stepY = 1 << shiftY; | ||
| 372 | |||
| 373 | if (dirty) { | ||
| 374 | GetAlphaRect(); | ||
| 375 | xl &= -stepX; xr |= stepX - 1; | ||
| 376 | yb &= -stepY; yt |= stepY - 1; | ||
| 377 | } | ||
| 378 | // The abbreviation 'ba' stands for basealpha. | ||
| 379 | |||
| 380 | |||
| 381 | // === Y/R plane — row-interleaved SoA: plane 0=ba (basealpha), 1=ry === | ||
| 382 | const uint16_t* y_row_ptr = soa_buf + yb * 4 * w_stride; | ||
| 383 | BYTE* buf_row = buf + pitch * yb; | ||
| 384 | |||
| 385 | if constexpr (bits_per_pixel == 8) { | ||
| 386 | for (int y = yb; y <= yt; ++y) { | ||
| 387 | const uint16_t* ba_y = y_row_ptr; | ||
| 388 | const uint16_t* ry_y = y_row_ptr + w_stride; | ||
| 389 | for (int x = xl; x <= xr; ++x) { | ||
| 390 | const int ba = ba_y[x]; | ||
| 391 | if (ba != 256) | ||
| 392 | buf_row[x] = BYTE((buf_row[x] * ba + ry_y[x]) >> 8); | ||
| 393 | } | ||
| 394 | buf_row += pitch; | ||
| 395 | y_row_ptr += 4 * w_stride; | ||
| 396 | } | ||
| 397 | } else if constexpr (bits_per_pixel >= 10 && bits_per_pixel <= 16) { | ||
| 398 | for (int y = yb; y <= yt; ++y) { | ||
| 399 | const uint16_t* ba_y = y_row_ptr; | ||
| 400 | const uint16_t* ry_y = y_row_ptr + w_stride; | ||
| 401 | uint16_t* p = reinterpret_cast<uint16_t*>(buf_row); | ||
| 402 | for (int x = xl; x <= xr; ++x) { | ||
| 403 | const int ba = ba_y[x]; | ||
| 404 | if (ba != 256) | ||
| 405 | p[x] = (uint16_t)((p[x] * ba + (ry_y[x] << (bits_per_pixel - 8))) >> 8); | ||
| 406 | } | ||
| 407 | buf_row += pitch; | ||
| 408 | y_row_ptr += 4 * w_stride; | ||
| 409 | } | ||
| 410 | } else { // float: Y/R plane assumes 0..1 scale | ||
| 411 | for (int y = yb; y <= yt; ++y) { | ||
| 412 | const uint16_t* ba_y = y_row_ptr; | ||
| 413 | const uint16_t* ry_y = y_row_ptr + w_stride; | ||
| 414 | float* p = reinterpret_cast<float*>(buf_row); | ||
| 415 | for (int x = xl; x <= xr; ++x) { | ||
| 416 | const int ba = ba_y[x]; | ||
| 417 | if (ba != 256) | ||
| 418 | p[x] = p[x] * ba / 256.0f + ry_y[x] / 65536.0f; | ||
| 419 | } | ||
| 420 | buf_row += pitch; | ||
| 421 | y_row_ptr += 4 * w_stride; | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | if (!bufU) return; | ||
| 426 | |||
| 427 | // === U/G and V/B planes — plane 2=u, 3=v; chroma-placement-aware downsampling === | ||
| 428 | const int uv_width = w >> shiftX; | ||
| 429 | const int xl_uv = xl >> shiftX; | ||
| 430 | const int xr_uv = xr >> shiftX; | ||
| 431 | // row preparation functions take full-width luma mask rows and downsample to UV width with correct chroma placement, returning a pointer to the prepared row (which may be buf_ba or an internal static buffer). | ||
| 432 | // rowprep output buffers (unused for MASK444 — rowprep returns input ptr directly). | ||
| 433 | std::vector<uint16_t>& buf_ba = uv_buf_ba; | ||
| 434 | std::vector<uint16_t>& buf_u = uv_buf_u; | ||
| 435 | std::vector<uint16_t>& buf_v = uv_buf_v; | ||
| 436 | |||
| 437 | const uint16_t* uv_row_ptr = soa_buf + yb * 4 * w_stride; | ||
| 438 | BYTE* bufU_row = bufU + pitchUV * (yb >> shiftY); | ||
| 439 | BYTE* bufV_row = bufV + pitchUV * (yb >> shiftY); | ||
| 440 | |||
| 441 | // mask_pitch for 420 modes: next luma row of same plane is 4*w_stride elements ahead in SoA layout | ||
| 442 | const int soa_row_pitch = 4 * w_stride; | ||
| 443 | |||
| 444 | // +1 * w_stride was already handled: Y | ||
| 445 | if constexpr (bits_per_pixel == 8) { | ||
| 446 | for (int y = yb; y <= yt; y += stepY) { | ||
| 447 | const uint16_t* ba_row = rowprep_fns[maskMode](uv_row_ptr, soa_row_pitch, uv_width, buf_ba, 0, 0, {}); | ||
| 448 | const uint16_t* u_row = rowprep_fns[maskMode](uv_row_ptr + 2 * w_stride, soa_row_pitch, uv_width, buf_u, 0, 0, {}); | ||
| 449 | const uint16_t* v_row = rowprep_fns[maskMode](uv_row_ptr + 3 * w_stride, soa_row_pitch, uv_width, buf_v, 0, 0, {}); | ||
| 450 | for (int xs = xl_uv; xs <= xr_uv; ++xs) { | ||
| 451 | const int ba = ba_row[xs]; | ||
| 452 | if (ba != 256) { | ||
| 453 | bufU_row[xs] = BYTE((bufU_row[xs] * ba + u_row[xs]) >> 8); | ||
| 454 | bufV_row[xs] = BYTE((bufV_row[xs] * ba + v_row[xs]) >> 8); | ||
| 455 | } | ||
| 456 | } | ||
| 457 | bufU_row += pitchUV; | ||
| 458 | bufV_row += pitchUV; | ||
| 459 | uv_row_ptr += stepY * 4 * w_stride; | ||
| 460 | } | ||
| 461 | } else if constexpr (bits_per_pixel >= 10 && bits_per_pixel <= 16) { | ||
| 462 | for (int y = yb; y <= yt; y += stepY) { | ||
| 463 | const uint16_t* ba_row = rowprep_fns[maskMode](uv_row_ptr, soa_row_pitch, uv_width, buf_ba, 0, 0, {}); | ||
| 464 | const uint16_t* u_row = rowprep_fns[maskMode](uv_row_ptr + 2 * w_stride, soa_row_pitch, uv_width, buf_u, 0, 0, {}); | ||
| 465 | const uint16_t* v_row = rowprep_fns[maskMode](uv_row_ptr + 3 * w_stride, soa_row_pitch, uv_width, buf_v, 0, 0, {}); | ||
| 466 | uint16_t* pU = reinterpret_cast<uint16_t*>(bufU_row); | ||
| 467 | uint16_t* pV = reinterpret_cast<uint16_t*>(bufV_row); | ||
| 468 | for (int xs = xl_uv; xs <= xr_uv; ++xs) { | ||
| 469 | const int ba = ba_row[xs]; | ||
| 470 | if (ba != 256) { | ||
| 471 | pU[xs] = (uint16_t)((pU[xs] * ba + (u_row[xs] << (bits_per_pixel - 8))) >> 8); | ||
| 472 | pV[xs] = (uint16_t)((pV[xs] * ba + (v_row[xs] << (bits_per_pixel - 8))) >> 8); | ||
| 473 | } | ||
| 474 | } | ||
| 475 | bufU_row += pitchUV; | ||
| 476 | bufV_row += pitchUV; | ||
| 477 | uv_row_ptr += stepY * 4 * w_stride; | ||
| 478 | } | ||
| 479 | } else { // float UV | ||
| 480 | // 32-bit float UV neutral chroma is 0.0f | ||
| 481 | const float middle_shift = isRGB ? 0.0f : 0.5f; // yes, correction needed in and out | ||
| 482 | constexpr float scale_1_per_256 = 1.0f / 256.0f; | ||
| 483 | constexpr float scale_1_per_65536 = 1.0f / 65536.0f; | ||
| 484 | for (int y = yb; y <= yt; y += stepY) { | ||
| 485 | const uint16_t* ba_row = rowprep_fns[maskMode](uv_row_ptr, soa_row_pitch, uv_width, buf_ba, 0, 0, {}); | ||
| 486 | const uint16_t* u_row = rowprep_fns[maskMode](uv_row_ptr + 2 * w_stride, soa_row_pitch, uv_width, buf_u, 0, 0, {}); | ||
| 487 | const uint16_t* v_row = rowprep_fns[maskMode](uv_row_ptr + 3 * w_stride, soa_row_pitch, uv_width, buf_v, 0, 0, {}); | ||
| 488 | float* pU = reinterpret_cast<float*>(bufU_row); | ||
| 489 | float* pV = reinterpret_cast<float*>(bufV_row); | ||
| 490 | for (int xs = xl_uv; xs <= xr_uv; ++xs) { | ||
| 491 | const int ba = ba_row[xs]; | ||
| 492 | if (ba != 256) { | ||
| 493 | const float ba_f = ba * scale_1_per_256; | ||
| 494 | pU[xs] = (pU[xs] + middle_shift) * ba_f + u_row[xs] * scale_1_per_65536 - middle_shift; | ||
| 495 | pV[xs] = (pV[xs] + middle_shift) * ba_f + v_row[xs] * scale_1_per_65536 - middle_shift; | ||
| 496 | } | ||
| 497 | } | ||
| 498 | bufU_row += pitchUV; | ||
| 499 | bufV_row += pitchUV; | ||
| 500 | uv_row_ptr += stepY * 4 * w_stride; | ||
| 501 | } | ||
| 502 | } | ||
| 503 | } | ||
| 504 | |||
| 505 | |||
| 506 | void Antialiaser::ApplyYUY2(BYTE* buf, int pitch) { | ||
| 507 | if (dirty) { | ||
| 508 | GetAlphaRect(); | ||
| 509 | xl &= -2; xr |= 1; | ||
| 510 | } | ||
| 511 | const uint16_t* row_ptr = soa_buf + yb * 4 * w_stride; | ||
| 512 | buf += pitch * yb; | ||
| 513 | |||
| 514 | for (int y = yb; y <= yt; ++y) { | ||
| 515 | const uint16_t* ba_row = row_ptr; | ||
| 516 | const uint16_t* ry_row = row_ptr + w_stride; | ||
| 517 | const uint16_t* u_row = row_ptr + 2 * w_stride; | ||
| 518 | const uint16_t* v_row = row_ptr + 3 * w_stride; | ||
| 519 | for (int x = xl; x <= xr; x += 2) { | ||
| 520 | const int ba0 = ba_row[x]; | ||
| 521 | const int ba1 = ba_row[x + 1]; | ||
| 522 | const int baUV = ba0 + ba1; | ||
| 523 | |||
| 524 | if (baUV != 512) { | ||
| 525 | buf[x*2+0] = BYTE((buf[x*2+0] * ba0 + ry_row[x]) >> 8); | ||
| 526 | buf[x*2+2] = BYTE((buf[x*2+2] * ba1 + ry_row[x + 1]) >> 8); | ||
| 527 | |||
| 528 | const int au = u_row[x] + u_row[x + 1]; | ||
| 529 | buf[x*2+1] = BYTE((buf[x*2+1] * baUV + au) >> 9); | ||
| 530 | |||
| 531 | const int av = v_row[x] + v_row[x + 1]; | ||
| 532 | buf[x*2+3] = BYTE((buf[x*2+3] * baUV + av) >> 9); | ||
| 533 | } | ||
| 534 | } | ||
| 535 | buf += pitch; | ||
| 536 | row_ptr += 4 * w_stride; | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | |||
| 541 | template<typename pixel_t, bool has_alpha> | ||
| 542 | void Antialiaser::ApplyRGB_packed(BYTE* buf, int pitch) | ||
| 543 | { | ||
| 544 | if (dirty) GetAlphaRect(); | ||
| 545 | const uint16_t* row_ptr = soa_buf + yb * 4 * w_stride; | ||
| 546 | buf += pitch * (h - yb - 1); // packed RGB is stored bottom-up | ||
| 547 | |||
| 548 | constexpr int pixel_step = has_alpha ? 4 : 3; | ||
| 549 | constexpr int alpha_shift = sizeof(pixel_t) == 1 ? 0 : 8; | ||
| 550 | for (int y = yb; y <= yt; ++y) { | ||
| 551 | const uint16_t* ba_row = row_ptr; | ||
| 552 | const uint16_t* ry_row = row_ptr + w_stride; // R addend | ||
| 553 | const uint16_t* gu_row = row_ptr + 2 * w_stride; // G addend | ||
| 554 | const uint16_t* bv_row = row_ptr + 3 * w_stride; // B addend | ||
| 555 | for (int x = xl; x <= xr; ++x) { | ||
| 556 | const int ba = ba_row[x]; | ||
| 557 | if (ba != 256) { | ||
| 558 | pixel_t* buf2 = reinterpret_cast<pixel_t*>(buf) + (x * pixel_step); | ||
| 559 | buf2[0] = (pixel_t)((buf2[0] * ba + (bv_row[x] << alpha_shift)) >> 8); // B | ||
| 560 | buf2[1] = (pixel_t)((buf2[1] * ba + (gu_row[x] << alpha_shift)) >> 8); // G | ||
| 561 | buf2[2] = (pixel_t)((buf2[2] * ba + (ry_row[x] << alpha_shift)) >> 8); // R | ||
| 562 | } | ||
| 563 | } | ||
| 564 | buf -= pitch; // packed RGB is bottom-up | ||
| 565 | row_ptr += 4 * w_stride; | ||
| 566 | } | ||
| 567 | } | ||
| 568 | |||
| 569 | |||
| 570 | void Antialiaser::GetAlphaRect() | ||
| 571 | { | ||
| 572 | dirty = false; | ||
| 573 | const int srcpitch = (w + 4 + 3) & -4; | ||
| 574 | #ifdef INTEL_INTRINSICS | ||
| 575 | if (getalpharect_fn) { | ||
| 576 | getalpharect_fn(lpAntialiasBits, soa_buf, w, h, w_stride, srcpitch, | ||
| 577 | textcolor, halocolor, xl, yt, xr, yb); | ||
| 578 | return; | ||
| 579 | } | ||
| 580 | #endif | ||
| 581 | GetAlphaRect_select_scalar(noaa, interlaced)( | ||
| 582 | lpAntialiasBits, soa_buf, w, h, w_stride, srcpitch, | ||
| 583 | textcolor, halocolor, xl, yt, xr, yb); | ||
| 584 | } | ||
| 585 | |||
| 586 | #endif // #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 587 | |||
| 588 | |||
| 589 | |||
| 590 | /************************************* | ||
| 591 | ******* Show Frame Number ****** | ||
| 592 | ************************************/ | ||
| 593 | |||
| 594 | ✗ | ShowFrameNumber::ShowFrameNumber(PClip _child, bool _scroll, int _offset, int _x, int _y, const char _fontname[], | |
| 595 | int _size, int _textcolor, int _halocolor, int font_width, int font_angle, | ||
| 596 | ✗ | bool _bold, bool _italic, bool _noaa, bool _gdi, IScriptEnvironment* env) | |
| 597 | : GenericVideoFilter(_child), | ||
| 598 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 599 | use_gdi(_gdi), | ||
| 600 | antialiaser(_gdi ? std::make_unique<Antialiaser>(vi.width, vi.height, _fontname, _size, | ||
| 601 | vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_textcolor) : _textcolor, | ||
| 602 | vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_halocolor) : _halocolor, | ||
| 603 | _bold, _italic, _noaa, | ||
| 604 | env->GetCPUFlagsEx(), ChromaLocation_e::AVS_CHROMA_CENTER /*center is quick*/, | ||
| 605 | font_width, font_angle, false) : nullptr), | ||
| 606 | #else | ||
| 607 | ✗ | use_gdi(false), | |
| 608 | #endif | ||
| 609 | ✗ | scroll(_scroll), offset(_offset), size(_size), x(_x), y(_y), | |
| 610 | ✗ | textcolor(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_textcolor) : _textcolor), | |
| 611 | ✗ | halocolor(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_halocolor) : _halocolor), | |
| 612 | ✗ | bold(_bold), italic(_italic), noaa(_noaa) | |
| 613 | { | ||
| 614 | ✗ | chromaplacement = ChromaLocation_e::AVS_CHROMA_LEFT; | |
| 615 | AVS_UNUSED(env); | ||
| 616 | |||
| 617 | ✗ | if (!use_gdi) { | |
| 618 | ✗ | current_font = GetBitmapFont(size, "Terminus", bold, false); | |
| 619 | ✗ | if (current_font == nullptr) { | |
| 620 | ✗ | current_font = GetBitmapFont(size, "", bold, false); | |
| 621 | ✗ | if (current_font == nullptr) | |
| 622 | ✗ | current_font = GetBitmapFont(size, "", !bold, false); | |
| 623 | } | ||
| 624 | } | ||
| 625 | ✗ | } | |
| 626 | |||
| 627 | enum { DefXY = (int)0x80000000 }; | ||
| 628 | |||
| 629 | ✗ | PVideoFrame ShowFrameNumber::GetFrame(int n, IScriptEnvironment* env) { | |
| 630 | ✗ | PVideoFrame frame = child->GetFrame(n, env); | |
| 631 | ✗ | n+=offset; | |
| 632 | ✗ | if (n < 0) return frame; | |
| 633 | |||
| 634 | char text[16]; | ||
| 635 | ✗ | snprintf(text, sizeof(text), "%05d", n); | |
| 636 | ✗ | text[15] = 0; | |
| 637 | |||
| 638 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 639 | if (use_gdi) { | ||
| 640 | HDC hdc = antialiaser->GetDC(); | ||
| 641 | if (!hdc) return frame; | ||
| 642 | env->MakeWritable(&frame); | ||
| 643 | RECT r = { 0, 0, 32767, 32767 }; | ||
| 644 | FillRect(hdc, &r, (HBRUSH)GetStockObject(BLACK_BRUSH)); | ||
| 645 | if (x!=DefXY || y!=DefXY) { | ||
| 646 | SetTextAlign(hdc, TA_BASELINE|TA_LEFT); | ||
| 647 | TextOut(hdc, x+16, y+16, text, (int)strlen(text)); | ||
| 648 | } else if (scroll) { | ||
| 649 | int n1 = vi.IsFieldBased() ? (n/2) : n; | ||
| 650 | int y2 = size + size * (n1 % (vi.height * 8 / size)); | ||
| 651 | SetTextAlign(hdc, TA_BASELINE | (child->GetParity(n) ? TA_LEFT : TA_RIGHT)); | ||
| 652 | TextOut(hdc, child->GetParity(n) ? 32 : vi.width*8+8, y2, text, (int)strlen(text)); | ||
| 653 | } else { | ||
| 654 | SetTextAlign(hdc, TA_BASELINE | (child->GetParity(n) ? TA_LEFT : TA_RIGHT)); | ||
| 655 | int text_len = (int)strlen(text); | ||
| 656 | for (int y2 = size; y2 < vi.height * 8; y2 += size) | ||
| 657 | TextOut(hdc, child->GetParity(n) ? 32 : vi.width * 8 + 8, y2, text, text_len); | ||
| 658 | } | ||
| 659 | GdiFlush(); | ||
| 660 | antialiaser->Apply(vi, &frame, frame->GetPitch()); | ||
| 661 | return frame; | ||
| 662 | } | ||
| 663 | #endif | ||
| 664 | ✗ | if (current_font == nullptr) | |
| 665 | ✗ | return frame; | |
| 666 | ✗ | env->MakeWritable(&frame); | |
| 667 | ✗ | std::string s_utf8 = charToUtf8(text, true); | |
| 668 | ✗ | if (x!=DefXY || y!=DefXY) { | |
| 669 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, x, y, s_utf8, false, textcolor, halocolor, true, 1, chromaplacement); | |
| 670 | ✗ | } else if (scroll) { | |
| 671 | ✗ | int n1 = vi.IsFieldBased() ? (n/2) : n; | |
| 672 | ✗ | int y2 = size + size * (n1 % (vi.height / size)); | |
| 673 | ✗ | if(child->GetParity(n)) | |
| 674 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, 4, y2, s_utf8, false, textcolor, halocolor, true, 1, chromaplacement); // left | |
| 675 | else | ||
| 676 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, vi.width - 1, y2, s_utf8, false, textcolor, halocolor, true, 3, chromaplacement); // right | |
| 677 | } else { | ||
| 678 | // size-1 because of bottom alignment | ||
| 679 | ✗ | for (int y2 = size - 1; y2 < vi.height; y2 += size) { | |
| 680 | ✗ | if (child->GetParity(n)) | |
| 681 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, 4, y2, s_utf8, false, textcolor, halocolor, true, 1, chromaplacement); // bottom-left | |
| 682 | else | ||
| 683 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, vi.width - 1, y2, s_utf8, false, textcolor, halocolor, true, 3, chromaplacement); // bottom-right | |
| 684 | } | ||
| 685 | } | ||
| 686 | ✗ | return frame; | |
| 687 | ✗ | } | |
| 688 | |||
| 689 | |||
| 690 | ✗ | AVSValue __cdecl ShowFrameNumber::Create(AVSValue args, void*, IScriptEnvironment* env) | |
| 691 | { | ||
| 692 | ✗ | PClip clip = args[0].AsClip(); | |
| 693 | ✗ | bool scroll = args[1].AsBool(false); | |
| 694 | ✗ | const int offset = args[2].AsInt(0); | |
| 695 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 696 | const bool gdi = args[14].AsBool(true); | ||
| 697 | const int x = args[3].IsFloat() ? int(args[3].AsFloat() * (gdi ? 8 : 1) + 0.5) : DefXY; | ||
| 698 | const int y = args[4].IsFloat() ? int(args[4].AsFloat() * (gdi ? 8 : 1) + 0.5) : DefXY; | ||
| 699 | const char* font = args[5].AsString(gdi ? "Arial" : "Terminus"); | ||
| 700 | const int size = int(args[6].AsFloat(24) * (gdi ? 8 : 1) + 0.5); | ||
| 701 | const int font_width = int(args[9].AsFloat(0) * (gdi ? 8 : 1) + 0.5); | ||
| 702 | const bool bold = args[11].AsBool(gdi); | ||
| 703 | #else | ||
| 704 | ✗ | const bool gdi = false; | |
| 705 | ✗ | const int x = args[3].IsFloat() ? int(args[3].AsFloat() + 0.5) : DefXY; | |
| 706 | ✗ | const int y = args[4].IsFloat() ? int(args[4].AsFloat() + 0.5) : DefXY; | |
| 707 | ✗ | const char* font = args[5].AsString("Terminus"); | |
| 708 | ✗ | const int size = int(args[6].AsFloat(24) + 0.5); | |
| 709 | ✗ | const int font_width = int(args[9].AsFloat(0) + 0.5); | |
| 710 | ✗ | const bool bold = args[11].AsBool(false); | |
| 711 | #endif | ||
| 712 | ✗ | const int text_color = args[7].AsInt(0xFFFF00); | |
| 713 | ✗ | const int halo_color = args[8].AsInt(0); | |
| 714 | ✗ | const int font_angle = int(args[10].AsFloat(0) * 10 + 0.5); | |
| 715 | ✗ | const bool italic = args[12].AsBool(false); | |
| 716 | ✗ | const bool noaa = args[13].AsBool(false); | |
| 717 | |||
| 718 | ✗ | if ((x==DefXY) ^ (y==DefXY)) | |
| 719 | ✗ | env->ThrowError("ShowFrameNumber: both x and y position must be specified"); | |
| 720 | |||
| 721 | ✗ | return new ShowFrameNumber(clip, scroll, offset, x, y, font, size, text_color, halo_color, font_width, font_angle, bold, italic, noaa, gdi, env); | |
| 722 | ✗ | } | |
| 723 | |||
| 724 | |||
| 725 | |||
| 726 | |||
| 727 | |||
| 728 | /************************************* | ||
| 729 | ******* Show CRC32 Number ****** | ||
| 730 | ************************************/ | ||
| 731 | |||
| 732 | ✗ | ShowCRC32::ShowCRC32(PClip _child, PClip _crc_child, bool _scroll, int _offset, int _x, int _y, | |
| 733 | const char _fontname[], int _size, int _textcolor, int _halocolor, int font_width, int font_angle, | ||
| 734 | bool _bold, bool _italic, bool _noaa, bool _gdi, | ||
| 735 | ✗ | const char* channels, int _mode, bool _compatible_mode, int _showmode, IScriptEnvironment* env) | |
| 736 | : GenericVideoFilter(_child), | ||
| 737 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 738 | use_gdi(_gdi), | ||
| 739 | antialiaser(_gdi ? std::make_unique<Antialiaser>(vi.width, vi.height, _fontname, _size, | ||
| 740 | vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_textcolor) : _textcolor, | ||
| 741 | vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_halocolor) : _halocolor, | ||
| 742 | _bold, _italic, _noaa, | ||
| 743 | env->GetCPUFlagsEx(), ChromaLocation_e::AVS_CHROMA_CENTER /*center is quick*/, | ||
| 744 | font_width, font_angle, false) : nullptr), | ||
| 745 | #else | ||
| 746 | ✗ | use_gdi(false), | |
| 747 | #endif | ||
| 748 | ✗ | crc_child(_crc_child), | |
| 749 | ✗ | mode(_mode), | |
| 750 | ✗ | compatible_mode(_compatible_mode), | |
| 751 | ✗ | showmode(_showmode), | |
| 752 | ✗ | scroll(_scroll), offset(_offset), size(_size), x(_x), y(_y), | |
| 753 | ✗ | textcolor(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_textcolor) : _textcolor), | |
| 754 | ✗ | halocolor(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_halocolor) : _halocolor), | |
| 755 | ✗ | bold(_bold), italic(_italic), noaa(_noaa) | |
| 756 | { | ||
| 757 | AVS_UNUSED(env); | ||
| 758 | |||
| 759 | ✗ | doB = doG = doR = doA = doY = doU = doV = false; | |
| 760 | ✗ | for (int k = 0; channels[k] != '\0'; ++k) { | |
| 761 | ✗ | switch (channels[k]) { | |
| 762 | ✗ | case 'B': case 'b': doB = true; break; | |
| 763 | ✗ | case 'G': case 'g': doG = true; break; | |
| 764 | ✗ | case 'R': case 'r': doR = true; break; | |
| 765 | ✗ | case 'A': case 'a': doA = true; break; | |
| 766 | ✗ | case 'Y': case 'y': doY = true; break; | |
| 767 | ✗ | case 'U': case 'u': doU = true; break; | |
| 768 | ✗ | case 'V': case 'v': doV = true; break; | |
| 769 | ✗ | default: break; | |
| 770 | } | ||
| 771 | } | ||
| 772 | |||
| 773 | ✗ | build_crc32_table(); | |
| 774 | |||
| 775 | ✗ | if (!use_gdi) { | |
| 776 | ✗ | current_font = GetBitmapFont(size, "Terminus", bold, false); | |
| 777 | ✗ | chromaplacement = ChromaLocation_e::AVS_CHROMA_LEFT; | |
| 778 | ✗ | if (current_font == nullptr) { | |
| 779 | ✗ | current_font = GetBitmapFont(size, "", bold, false); | |
| 780 | ✗ | if (current_font == nullptr) | |
| 781 | ✗ | current_font = GetBitmapFont(size, "", !bold, false); | |
| 782 | } | ||
| 783 | } | ||
| 784 | ✗ | } | |
| 785 | |||
| 786 | ✗ | void ShowCRC32::build_crc32_table(void) { | |
| 787 | ✗ | for (uint32_t i = 0; i < 256; i++) { | |
| 788 | ✗ | uint32_t ch = i; | |
| 789 | ✗ | uint32_t crc = 0; | |
| 790 | ✗ | for (size_t j = 0; j < 8; j++) { | |
| 791 | ✗ | uint32_t b = (ch ^ crc) & 1; | |
| 792 | ✗ | crc >>= 1; | |
| 793 | ✗ | if (b) crc = crc ^ 0xEDB88320; | |
| 794 | ✗ | ch >>= 1; | |
| 795 | } | ||
| 796 | ✗ | crc32_table[i] = crc; | |
| 797 | } | ||
| 798 | ✗ | } | |
| 799 | |||
| 800 | ✗ | std::string ShowCRC32::compute_crc_text(PVideoFrame& crc_frame, std::vector<uint32_t>& out_values) const { | |
| 801 | ✗ | out_values.clear(); | |
| 802 | char buf[16]; | ||
| 803 | |||
| 804 | ✗ | if (compatible_mode) { | |
| 805 | // Packed format, default parameters: hash the raw interleaved buffer directly. | ||
| 806 | ✗ | const uint8_t* ptr = crc_frame->GetReadPtr(); | |
| 807 | ✗ | int rowsize = crc_frame->GetRowSize(); | |
| 808 | ✗ | int pitch = crc_frame->GetPitch(); | |
| 809 | ✗ | int height = crc_frame->GetHeight(); | |
| 810 | ✗ | uint32_t crc = 0xFFFFFFFF; | |
| 811 | ✗ | for (int y = 0; y < height; y++) { | |
| 812 | ✗ | for (int x = 0; x < rowsize; x++) { | |
| 813 | ✗ | uint32_t t = (ptr[x] ^ crc) & 0xFF; | |
| 814 | ✗ | crc = (crc >> 8) ^ crc32_table[t]; | |
| 815 | } | ||
| 816 | ✗ | ptr += pitch; | |
| 817 | } | ||
| 818 | ✗ | crc = ~crc; | |
| 819 | ✗ | out_values.push_back(crc); | |
| 820 | ✗ | snprintf(buf, sizeof(buf), "%08X", (unsigned int)crc); | |
| 821 | ✗ | return buf; | |
| 822 | } | ||
| 823 | |||
| 824 | ✗ | const VideoInfo& crc_vi = crc_child->GetVideoInfo(); | |
| 825 | |||
| 826 | struct PlaneEntry { int id; const char* label; bool active; }; | ||
| 827 | PlaneEntry entries[4]; | ||
| 828 | ✗ | int nentries = 0; | |
| 829 | |||
| 830 | ✗ | if (crc_vi.IsYUV() || crc_vi.IsYUVA()) { | |
| 831 | ✗ | entries[nentries++] = { PLANAR_Y, "Y", doY }; | |
| 832 | ✗ | if (crc_vi.NumComponents() > 1) { | |
| 833 | ✗ | entries[nentries++] = { PLANAR_U, "U", doU }; | |
| 834 | ✗ | entries[nentries++] = { PLANAR_V, "V", doV }; | |
| 835 | } | ||
| 836 | ✗ | if (crc_vi.IsYUVA()) | |
| 837 | ✗ | entries[nentries++] = { PLANAR_A, "A", doA }; | |
| 838 | } else { // PlanarRGB(A) — displayed in logical R,G,B,A order regardless of physical plane order | ||
| 839 | ✗ | entries[nentries++] = { PLANAR_R, "R", doR }; | |
| 840 | ✗ | entries[nentries++] = { PLANAR_G, "G", doG }; | |
| 841 | ✗ | entries[nentries++] = { PLANAR_B, "B", doB }; | |
| 842 | ✗ | if (crc_vi.IsPlanarRGBA()) | |
| 843 | ✗ | entries[nentries++] = { PLANAR_A, "A", doA }; | |
| 844 | } | ||
| 845 | |||
| 846 | ✗ | if (mode == 0) { | |
| 847 | // Combined CRC over all selected planes in order | ||
| 848 | ✗ | uint32_t crc = 0xFFFFFFFF; | |
| 849 | ✗ | for (int i = 0; i < nentries; i++) { | |
| 850 | ✗ | if (!entries[i].active) continue; | |
| 851 | ✗ | const uint8_t* ptr = crc_frame->GetReadPtr(entries[i].id); | |
| 852 | ✗ | int rowsize = crc_frame->GetRowSize(entries[i].id); | |
| 853 | ✗ | int pitch = crc_frame->GetPitch(entries[i].id); | |
| 854 | ✗ | int height = crc_frame->GetHeight(entries[i].id); | |
| 855 | ✗ | for (int y = 0; y < height; y++) { | |
| 856 | ✗ | for (int x = 0; x < rowsize; x++) { | |
| 857 | ✗ | uint32_t t = (ptr[x] ^ crc) & 0xFF; | |
| 858 | ✗ | crc = (crc >> 8) ^ crc32_table[t]; | |
| 859 | } | ||
| 860 | ✗ | ptr += pitch; | |
| 861 | } | ||
| 862 | } | ||
| 863 | ✗ | crc = ~crc; | |
| 864 | ✗ | out_values.push_back(crc); | |
| 865 | ✗ | snprintf(buf, sizeof(buf), "%08X", (unsigned int)crc); | |
| 866 | ✗ | return buf; | |
| 867 | } else { | ||
| 868 | // Separate CRC per plane, displayed as "Y:XXXXXXXX U:XXXXXXXX ..." | ||
| 869 | ✗ | std::string result; | |
| 870 | ✗ | for (int i = 0; i < nentries; i++) { | |
| 871 | ✗ | if (!entries[i].active) continue; | |
| 872 | ✗ | const uint8_t* ptr = crc_frame->GetReadPtr(entries[i].id); | |
| 873 | ✗ | int rowsize = crc_frame->GetRowSize(entries[i].id); | |
| 874 | ✗ | int pitch = crc_frame->GetPitch(entries[i].id); | |
| 875 | ✗ | int height = crc_frame->GetHeight(entries[i].id); | |
| 876 | ✗ | uint32_t crc = 0xFFFFFFFF; | |
| 877 | ✗ | for (int y = 0; y < height; y++) { | |
| 878 | ✗ | for (int x = 0; x < rowsize; x++) { | |
| 879 | ✗ | uint32_t t = (ptr[x] ^ crc) & 0xFF; | |
| 880 | ✗ | crc = (crc >> 8) ^ crc32_table[t]; | |
| 881 | } | ||
| 882 | ✗ | ptr += pitch; | |
| 883 | } | ||
| 884 | ✗ | crc = ~crc; | |
| 885 | ✗ | out_values.push_back(crc); | |
| 886 | ✗ | if (!result.empty()) result += ' '; | |
| 887 | ✗ | snprintf(buf, sizeof(buf), "%s:%08X", entries[i].label, (unsigned int)crc); | |
| 888 | ✗ | result += buf; | |
| 889 | } | ||
| 890 | ✗ | return result; | |
| 891 | ✗ | } | |
| 892 | } | ||
| 893 | |||
| 894 | |||
| 895 | ✗ | PVideoFrame ShowCRC32::GetFrame(int n, IScriptEnvironment* env) { | |
| 896 | ✗ | PVideoFrame frame = child->GetFrame(n, env); | |
| 897 | ✗ | PVideoFrame crc_frame = crc_child->GetFrame(n, env); | |
| 898 | ✗ | n += offset; | |
| 899 | ✗ | if (n < 0) return frame; | |
| 900 | |||
| 901 | ✗ | std::vector<uint32_t> crc_vals; | |
| 902 | ✗ | std::string crc_str = compute_crc_text(crc_frame, crc_vals); | |
| 903 | |||
| 904 | ✗ | if (showmode >= 1) { | |
| 905 | ✗ | env->MakePropertyWritable(&frame); | |
| 906 | ✗ | AVSMap* avsmap = env->getFramePropsRW(frame); | |
| 907 | ✗ | std::vector<int64_t> int64array(crc_vals.size()); | |
| 908 | ✗ | for (size_t i = 0; i < crc_vals.size(); i++) | |
| 909 | ✗ | int64array[i] = static_cast<int64_t>(crc_vals[i]); // uint32 fits non-negative in int64 | |
| 910 | ✗ | env->propSetIntArray(avsmap, "ShowCRC32", int64array.data(), (int)int64array.size()); | |
| 911 | ✗ | } | |
| 912 | |||
| 913 | ✗ | if (showmode == 2) | |
| 914 | ✗ | return frame; | |
| 915 | |||
| 916 | ✗ | const char* text = crc_str.c_str(); | |
| 917 | |||
| 918 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 919 | if (use_gdi) { | ||
| 920 | HDC hdc = antialiaser->GetDC(); | ||
| 921 | if (!hdc) return frame; | ||
| 922 | env->MakeWritable(&frame); | ||
| 923 | RECT r = { 0, 0, 32767, 32767 }; | ||
| 924 | FillRect(hdc, &r, (HBRUSH)GetStockObject(BLACK_BRUSH)); | ||
| 925 | if (x != DefXY || y != DefXY) { | ||
| 926 | SetTextAlign(hdc, TA_BASELINE | TA_LEFT); | ||
| 927 | TextOut(hdc, x + 16, y + 16, text, (int)strlen(text)); | ||
| 928 | } else if (scroll) { | ||
| 929 | int n1 = vi.IsFieldBased() ? (n / 2) : n; | ||
| 930 | int y2 = size + size * (n1 % (vi.height * 8 / size)); | ||
| 931 | SetTextAlign(hdc, TA_BASELINE | (child->GetParity(n) ? TA_LEFT : TA_RIGHT)); | ||
| 932 | TextOut(hdc, child->GetParity(n) ? 32 : vi.width * 8 + 8, y2, text, (int)strlen(text)); | ||
| 933 | } else { | ||
| 934 | SetTextAlign(hdc, TA_BASELINE | (child->GetParity(n) ? TA_LEFT : TA_RIGHT)); | ||
| 935 | int text_len = (int)strlen(text); | ||
| 936 | for (int y2 = size; y2 < vi.height * 8; y2 += size) | ||
| 937 | TextOut(hdc, child->GetParity(n) ? 32 : vi.width * 8 + 8, y2, text, text_len); | ||
| 938 | } | ||
| 939 | GdiFlush(); | ||
| 940 | antialiaser->Apply(vi, &frame, frame->GetPitch()); | ||
| 941 | return frame; | ||
| 942 | } | ||
| 943 | #endif | ||
| 944 | ✗ | if (current_font == nullptr) | |
| 945 | ✗ | return frame; | |
| 946 | ✗ | env->MakeWritable(&frame); | |
| 947 | ✗ | std::string s_utf8 = charToUtf8(text, true); | |
| 948 | ✗ | if (x != DefXY || y != DefXY) { | |
| 949 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, x, y, s_utf8, false, textcolor, halocolor, true, 1, chromaplacement); | |
| 950 | ✗ | } else if (scroll) { | |
| 951 | ✗ | int n1 = vi.IsFieldBased() ? (n / 2) : n; | |
| 952 | ✗ | int y2 = size + size * (n1 % (vi.height / size)); | |
| 953 | ✗ | if (child->GetParity(n)) | |
| 954 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, 4, y2, s_utf8, false, textcolor, halocolor, true, 1, chromaplacement); // left | |
| 955 | else | ||
| 956 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, vi.width - 1, y2, s_utf8, false, textcolor, halocolor, true, 3, chromaplacement); // right | |
| 957 | } else { | ||
| 958 | ✗ | for (int y2 = size; y2 < vi.height; y2 += size) { | |
| 959 | ✗ | if (child->GetParity(n)) | |
| 960 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, 4, y2, s_utf8, false, textcolor, halocolor, true, 1, chromaplacement); // left | |
| 961 | else | ||
| 962 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, vi.width - 1, y2, s_utf8, false, textcolor, halocolor, true, 3, chromaplacement); // right | |
| 963 | } | ||
| 964 | } | ||
| 965 | ✗ | return frame; | |
| 966 | ✗ | } | |
| 967 | |||
| 968 | |||
| 969 | ✗ | AVSValue __cdecl ShowCRC32::Create(AVSValue args, void*, IScriptEnvironment* env) | |
| 970 | { | ||
| 971 | ✗ | PClip original = args[0].AsClip(); | |
| 972 | ✗ | const VideoInfo& vi = original->GetVideoInfo(); | |
| 973 | |||
| 974 | // Compatible mode: packed format, no explicit channels/mode — hash raw interleaved buffer. | ||
| 975 | ✗ | const bool is_packed = vi.IsYUY2() || vi.IsRGB24() || vi.IsRGB32() || vi.IsRGB48() || vi.IsRGB64(); | |
| 976 | ✗ | const bool compatible_mode = is_packed && !args[14].Defined() && !args[15].Defined(); | |
| 977 | |||
| 978 | // Build a planar CRC child so per-plane access works for packed formats. | ||
| 979 | ✗ | PClip crc_child; | |
| 980 | ✗ | if (!compatible_mode && vi.IsYUY2()) { | |
| 981 | ✗ | AVSValue a[1] = { original }; | |
| 982 | ✗ | crc_child = env->Invoke("ConvertToYV16", AVSValue(a, 1)).AsClip(); | |
| 983 | ✗ | } else if (!compatible_mode && (vi.IsRGB32() || vi.IsRGB64())) { | |
| 984 | ✗ | AVSValue a[1] = { original }; | |
| 985 | ✗ | crc_child = env->Invoke("ConvertToPlanarRGBA", AVSValue(a, 1)).AsClip(); | |
| 986 | ✗ | } else if (!compatible_mode && (vi.IsRGB24() || vi.IsRGB48())) { | |
| 987 | ✗ | AVSValue a[1] = { original }; | |
| 988 | ✗ | crc_child = env->Invoke("ConvertToPlanarRGB", AVSValue(a, 1)).AsClip(); | |
| 989 | ✗ | } else { | |
| 990 | ✗ | crc_child = original; | |
| 991 | } | ||
| 992 | |||
| 993 | ✗ | const VideoInfo& crc_vi = crc_child->GetVideoInfo(); | |
| 994 | ✗ | const char* default_channels = crc_vi.IsRGB() ? "RGBA" : "YUVA"; | |
| 995 | ✗ | const char* channels = args[14].AsString(default_channels); | |
| 996 | ✗ | const int mode = args[15].AsInt(0); | |
| 997 | ✗ | const int showmode = args[16].AsInt(0); | |
| 998 | ✗ | if (showmode < 0 || showmode > 2) | |
| 999 | ✗ | env->ThrowError("ShowCRC32: showmode must be 0, 1 or 2"); | |
| 1000 | |||
| 1001 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 1002 | const bool gdi = args[17].AsBool(true); | ||
| 1003 | const int x = args[3].IsFloat() ? int(args[3].AsFloat() * (gdi ? 8 : 1) + 0.5) : DefXY; | ||
| 1004 | const int y = args[4].IsFloat() ? int(args[4].AsFloat() * (gdi ? 8 : 1) + 0.5) : DefXY; | ||
| 1005 | const char* font = args[5].AsString(gdi ? "Arial" : "Terminus"); | ||
| 1006 | const int size = int(args[6].AsFloat(24) * (gdi ? 8 : 1) + 0.5); | ||
| 1007 | const int font_width = int(args[9].AsFloat(0) * (gdi ? 8 : 1) + 0.5); | ||
| 1008 | const bool bold = args[11].AsBool(gdi); | ||
| 1009 | #else | ||
| 1010 | ✗ | const bool gdi = false; | |
| 1011 | ✗ | const int x = args[3].IsFloat() ? int(args[3].AsFloat() + 0.5) : DefXY; | |
| 1012 | ✗ | const int y = args[4].IsFloat() ? int(args[4].AsFloat() + 0.5) : DefXY; | |
| 1013 | ✗ | const char* font = args[5].AsString("Terminus"); | |
| 1014 | ✗ | const int size = int(args[6].AsFloat(24) + 0.5); | |
| 1015 | ✗ | const int font_width = int(args[9].AsFloat(0) + 0.5); | |
| 1016 | ✗ | const bool bold = args[11].AsBool(false); | |
| 1017 | #endif | ||
| 1018 | ✗ | const int text_color = args[7].AsInt(0xFFFF00); | |
| 1019 | ✗ | const int halo_color = args[8].AsInt(0); | |
| 1020 | ✗ | const int font_angle = int(args[10].AsFloat(0) * 10 + 0.5); | |
| 1021 | ✗ | const bool italic = args[12].AsBool(false); | |
| 1022 | ✗ | const bool noaa = args[13].AsBool(false); | |
| 1023 | |||
| 1024 | ✗ | if ((x == DefXY) ^ (y == DefXY)) | |
| 1025 | ✗ | env->ThrowError("ShowCRC32: both x and y position must be specified"); | |
| 1026 | |||
| 1027 | return new ShowCRC32(original, crc_child, | ||
| 1028 | ✗ | args[1].AsBool(false), args[2].AsInt(0), | |
| 1029 | x, y, font, size, text_color, halo_color, font_width, font_angle, | ||
| 1030 | ✗ | bold, italic, noaa, gdi, channels, mode, compatible_mode, showmode, env); | |
| 1031 | ✗ | } | |
| 1032 | |||
| 1033 | |||
| 1034 | |||
| 1035 | |||
| 1036 | |||
| 1037 | |||
| 1038 | |||
| 1039 | /*********************************** | ||
| 1040 | ******* Show SMPTE code ****** | ||
| 1041 | **********************************/ | ||
| 1042 | |||
| 1043 | ✗ | ShowSMPTE::ShowSMPTE(PClip _child, double _rate, const char* offset, int _offset_f, int _x, int _y, const char _fontname[], | |
| 1044 | ✗ | int _size, int _textcolor, int _halocolor, int font_width, int font_angle, bool _bold, bool _italic, bool _noaa, bool _gdi, IScriptEnvironment* env) | |
| 1045 | : GenericVideoFilter(_child), | ||
| 1046 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 1047 | use_gdi(_gdi), | ||
| 1048 | antialiaser(_gdi ? std::make_unique<Antialiaser>(vi.width, vi.height, _fontname, _size, | ||
| 1049 | vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_textcolor) : _textcolor, | ||
| 1050 | vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_halocolor) : _halocolor, | ||
| 1051 | _bold, _italic, _noaa, | ||
| 1052 | env->GetCPUFlagsEx(), ChromaLocation_e::AVS_CHROMA_CENTER /*center is quick*/, | ||
| 1053 | font_width, font_angle, false) : nullptr), | ||
| 1054 | #else | ||
| 1055 | ✗ | use_gdi(false), | |
| 1056 | #endif | ||
| 1057 | ✗ | x(_x), y(_y), | |
| 1058 | ✗ | textcolor(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_textcolor) : _textcolor), | |
| 1059 | ✗ | halocolor(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_halocolor) : _halocolor), | |
| 1060 | ✗ | bold(_bold), italic(_italic), noaa(_noaa) | |
| 1061 | { | ||
| 1062 | ✗ | if (!use_gdi) { | |
| 1063 | ✗ | current_font = GetBitmapFont(_size, "Terminus", bold, false); | |
| 1064 | ✗ | chromaplacement = ChromaLocation_e::AVS_CHROMA_LEFT; | |
| 1065 | ✗ | if (current_font == nullptr) { | |
| 1066 | ✗ | current_font = GetBitmapFont(_size, "", bold, false); | |
| 1067 | ✗ | if (current_font == nullptr) | |
| 1068 | ✗ | current_font = GetBitmapFont(_size, "", !bold, false); | |
| 1069 | } | ||
| 1070 | } | ||
| 1071 | int off_f, off_sec, off_min, off_hour; | ||
| 1072 | |||
| 1073 | ✗ | rate = int(_rate + 0.5); | |
| 1074 | ✗ | dropframe = false; | |
| 1075 | ✗ | if (_rate > 23.975 && _rate < 23.977) { // Pulldown drop frame rate | |
| 1076 | ✗ | rate = 24; | |
| 1077 | ✗ | dropframe = true; | |
| 1078 | } | ||
| 1079 | ✗ | else if (_rate > 29.969 && _rate < 29.971) { | |
| 1080 | ✗ | rate = 30; | |
| 1081 | ✗ | dropframe = true; | |
| 1082 | } | ||
| 1083 | ✗ | else if (_rate > 47.951 && _rate < 47.953) { | |
| 1084 | ✗ | rate = 48; | |
| 1085 | ✗ | dropframe = true; | |
| 1086 | } | ||
| 1087 | ✗ | else if (_rate > 59.939 && _rate < 59.941) { | |
| 1088 | ✗ | rate = 60; | |
| 1089 | ✗ | dropframe = true; | |
| 1090 | } | ||
| 1091 | ✗ | else if (_rate > 119.879 && _rate < 119.881) { | |
| 1092 | ✗ | rate = 120; | |
| 1093 | ✗ | dropframe = true; | |
| 1094 | } | ||
| 1095 | ✗ | else if (fabs(_rate - rate) > 0.001) { | |
| 1096 | ✗ | env->ThrowError("ShowSMPTE: rate argument must be 23.976, 29.97 or an integer"); | |
| 1097 | } | ||
| 1098 | |||
| 1099 | ✗ | if (offset) { | |
| 1100 | ✗ | if (strlen(offset)!=11 || offset[2] != ':' || offset[5] != ':' || offset[8] != ':') | |
| 1101 | ✗ | env->ThrowError("ShowSMPTE: offset should be of the form \"00:00:00:00\" "); | |
| 1102 | ✗ | if (!isdigit(offset[0]) || !isdigit(offset[1]) || !isdigit(offset[3]) || !isdigit(offset[4]) | |
| 1103 | ✗ | || !isdigit(offset[6]) || !isdigit(offset[7]) || !isdigit(offset[9]) || !isdigit(offset[10])) | |
| 1104 | ✗ | env->ThrowError("ShowSMPTE: offset should be of the form \"00:00:00:00\" "); | |
| 1105 | |||
| 1106 | ✗ | off_hour = atoi(offset); | |
| 1107 | |||
| 1108 | ✗ | off_min = atoi(offset+3); | |
| 1109 | ✗ | if (off_min > 59) | |
| 1110 | ✗ | env->ThrowError("ShowSMPTE: make sure that the number of minutes in the offset is in the range 0..59"); | |
| 1111 | |||
| 1112 | ✗ | off_sec = atoi(offset+6); | |
| 1113 | ✗ | if (off_sec > 59) | |
| 1114 | ✗ | env->ThrowError("ShowSMPTE: make sure that the number of seconds in the offset is in the range 0..59"); | |
| 1115 | |||
| 1116 | ✗ | off_f = atoi(offset+9); | |
| 1117 | ✗ | if (off_f >= rate) | |
| 1118 | ✗ | env->ThrowError("ShowSMPTE: make sure that the number of frames in the offset is in the range 0..%d", rate-1); | |
| 1119 | |||
| 1120 | ✗ | offset_f = off_f + rate*(off_sec + 60*off_min + 3600*off_hour); | |
| 1121 | ✗ | if (dropframe) { | |
| 1122 | ✗ | if (rate == 30) { | |
| 1123 | ✗ | int c = 0; | |
| 1124 | ✗ | c = off_min + 60*off_hour; // number of drop events | |
| 1125 | ✗ | c -= c/10; // less non-drop events on 10 minutes | |
| 1126 | ✗ | c *=2; // drop 2 frames per drop event | |
| 1127 | ✗ | offset_f -= c; | |
| 1128 | } | ||
| 1129 | else { | ||
| 1130 | // Need to cogitate with the guys about this | ||
| 1131 | // gotta drop 86.3 counts per hour. So until | ||
| 1132 | // a proper formula is found, just wing it! | ||
| 1133 | ✗ | offset_f -= 2 * ((offset_f+1001)/2002); | |
| 1134 | } | ||
| 1135 | } | ||
| 1136 | } | ||
| 1137 | else { | ||
| 1138 | ✗ | offset_f = _offset_f; | |
| 1139 | } | ||
| 1140 | ✗ | } | |
| 1141 | |||
| 1142 | |||
| 1143 | ✗ | PVideoFrame __stdcall ShowSMPTE::GetFrame(int n, IScriptEnvironment* env) | |
| 1144 | { | ||
| 1145 | ✗ | PVideoFrame frame = child->GetFrame(n, env); | |
| 1146 | ✗ | n+=offset_f; | |
| 1147 | ✗ | if (n < 0) return frame; | |
| 1148 | |||
| 1149 | ✗ | if (dropframe) { | |
| 1150 | ✗ | if ((rate == 30) || (rate == 60) || (rate == 120)) { | |
| 1151 | // at 10:00, 20:00, 30:00, etc. nothing should happen if offset=0 | ||
| 1152 | ✗ | const int f = rate/30; | |
| 1153 | ✗ | const int r = n % f; | |
| 1154 | ✗ | n /= f; | |
| 1155 | |||
| 1156 | ✗ | const int high = n / 17982; | |
| 1157 | ✗ | int low = n % 17982; | |
| 1158 | ✗ | if (low>=2) | |
| 1159 | ✗ | low += 2 * ((low-2) / 1798); | |
| 1160 | ✗ | n = high * 18000 + low; | |
| 1161 | |||
| 1162 | ✗ | n = f*n + r; | |
| 1163 | ✗ | } | |
| 1164 | else { | ||
| 1165 | // Needs some cogitating | ||
| 1166 | ✗ | n += 2 * ((n+1001)/2002); | |
| 1167 | } | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | char text[32]; | ||
| 1171 | |||
| 1172 | ✗ | if (rate > 0) { | |
| 1173 | ✗ | int frames = n % rate; | |
| 1174 | ✗ | int sec = n/rate; | |
| 1175 | ✗ | int min = sec/60; | |
| 1176 | ✗ | int hour = sec/3600; | |
| 1177 | |||
| 1178 | ✗ | snprintf(text, sizeof(text), | |
| 1179 | ✗ | rate>99 ? "%02d:%02d:%02d:%03d" : "%02d:%02d:%02d:%02d", | |
| 1180 | hour, min%60, sec%60, frames); | ||
| 1181 | } | ||
| 1182 | else { | ||
| 1183 | ✗ | int ms = (int)(((int64_t)n * vi.fps_denominator * 1000 / vi.fps_numerator)%1000); | |
| 1184 | ✗ | int sec = (int)((int64_t)n * vi.fps_denominator / vi.fps_numerator); | |
| 1185 | ✗ | int min = sec/60; | |
| 1186 | ✗ | int hour = sec/3600; | |
| 1187 | |||
| 1188 | ✗ | snprintf(text, sizeof(text), "%02d:%02d:%02d.%03d", hour, min%60, sec%60, ms); | |
| 1189 | } | ||
| 1190 | ✗ | text[15] = 0; | |
| 1191 | |||
| 1192 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 1193 | if (use_gdi) { | ||
| 1194 | HDC hdc = antialiaser->GetDC(); | ||
| 1195 | if (!hdc) return frame; | ||
| 1196 | env->MakeWritable(&frame); | ||
| 1197 | SetTextAlign(hdc, TA_BASELINE|TA_CENTER); | ||
| 1198 | TextOut(hdc, x+16, y+16, text, (int)strlen(text)); | ||
| 1199 | GdiFlush(); | ||
| 1200 | antialiaser->Apply(vi, &frame, frame->GetPitch()); | ||
| 1201 | return frame; | ||
| 1202 | } | ||
| 1203 | #endif | ||
| 1204 | ✗ | if (current_font == nullptr) | |
| 1205 | ✗ | return frame; | |
| 1206 | ✗ | env->MakeWritable(&frame); | |
| 1207 | ✗ | const bool utf8 = true; | |
| 1208 | ✗ | auto s_utf8 = charToUtf8(text, utf8); | |
| 1209 | ✗ | SimpleTextOutW(current_font.get(), vi, frame, x + 2, y + 2, s_utf8, true, textcolor, halocolor, false, 2 /* V baseline H center */, chromaplacement); | |
| 1210 | ✗ | return frame; | |
| 1211 | ✗ | } | |
| 1212 | |||
| 1213 | ✗ | AVSValue __cdecl ShowSMPTE::CreateSMTPE(AVSValue args, void*, IScriptEnvironment* env) | |
| 1214 | { | ||
| 1215 | ✗ | PClip clip = args[0].AsClip(); | |
| 1216 | ✗ | const VideoInfo& arg0vi = args[0].AsClip()->GetVideoInfo(); | |
| 1217 | ✗ | double def_rate = (double)arg0vi.fps_numerator / arg0vi.fps_denominator; | |
| 1218 | ✗ | double dfrate = args[1].AsDblDef(def_rate); | |
| 1219 | ✗ | const char* offset = args[2].AsString(0); | |
| 1220 | ✗ | const int offset_f = args[3].AsInt(0); | |
| 1221 | ✗ | const int xreal = arg0vi.width/2; | |
| 1222 | ✗ | const int yreal = arg0vi.height-8; | |
| 1223 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 1224 | const bool gdi = args[15].AsBool(true); | ||
| 1225 | const int x = int(args[4].AsDblDef(xreal) * (gdi ? 8 : 1) + 0.5); | ||
| 1226 | const int y = int(args[5].AsDblDef(yreal) * (gdi ? 8 : 1) + 0.5); | ||
| 1227 | const char* font = args[6].AsString(gdi ? "Arial" : "Terminus"); | ||
| 1228 | const int size = int(args[7].AsFloat(24) * (gdi ? 8 : 1) + 0.5); | ||
| 1229 | const int font_width = int(args[10].AsFloat(0) * (gdi ? 8 : 1) + 0.5); | ||
| 1230 | const bool bold = args[12].AsBool(gdi); | ||
| 1231 | #else | ||
| 1232 | ✗ | const bool gdi = false; | |
| 1233 | ✗ | const int x = int(args[4].AsDblDef(xreal) + 0.5); | |
| 1234 | ✗ | const int y = int(args[5].AsDblDef(yreal) + 0.5); | |
| 1235 | ✗ | const char* font = args[6].AsString("Terminus"); | |
| 1236 | ✗ | const int size = int(args[7].AsFloat(24) + 0.5); | |
| 1237 | ✗ | const int font_width = int(args[10].AsFloat(0) + 0.5); | |
| 1238 | ✗ | const bool bold = args[12].AsBool(false); | |
| 1239 | #endif | ||
| 1240 | ✗ | const int text_color = args[8].AsInt(0xFFFF00); | |
| 1241 | ✗ | const int halo_color = args[9].AsInt(0); | |
| 1242 | ✗ | const int font_angle = int(args[11].AsFloat(0)*10+0.5); | |
| 1243 | ✗ | const bool italic = args[13].AsBool(false); | |
| 1244 | ✗ | const bool noaa = args[14].AsBool(false); | |
| 1245 | |||
| 1246 | ✗ | return new ShowSMPTE(clip, dfrate, offset, offset_f, x, y, font, size, text_color, halo_color, font_width, font_angle, bold, italic, noaa, gdi, env); | |
| 1247 | ✗ | } | |
| 1248 | |||
| 1249 | ✗ | AVSValue __cdecl ShowSMPTE::CreateTime(AVSValue args, void*, IScriptEnvironment* env) | |
| 1250 | { | ||
| 1251 | ✗ | PClip clip = args[0].AsClip(); | |
| 1252 | ✗ | const int offset_f = args[1].AsInt(0); | |
| 1253 | ✗ | const int xreal = args[0].AsClip()->GetVideoInfo().width/2; | |
| 1254 | ✗ | const int yreal = args[0].AsClip()->GetVideoInfo().height-8; | |
| 1255 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 1256 | const bool gdi = args[13].AsBool(true); | ||
| 1257 | const int x = int(args[2].AsDblDef(xreal) * (gdi ? 8 : 1) + 0.5); | ||
| 1258 | const int y = int(args[3].AsDblDef(yreal) * (gdi ? 8 : 1) + 0.5); | ||
| 1259 | const char* font = args[4].AsString(gdi ? "Arial" : "Terminus"); | ||
| 1260 | const int size = int(args[5].AsFloat(24) * (gdi ? 8 : 1) + 0.5); | ||
| 1261 | const int font_width = int(args[8].AsFloat(0) * (gdi ? 8 : 1) + 0.5); | ||
| 1262 | const bool bold = args[10].AsBool(gdi); | ||
| 1263 | #else | ||
| 1264 | ✗ | const bool gdi = false; | |
| 1265 | ✗ | const int x = int(args[2].AsDblDef(xreal) + 0.5); | |
| 1266 | ✗ | const int y = int(args[3].AsDblDef(yreal) + 0.5); | |
| 1267 | ✗ | const char* font = args[4].AsString("Terminus"); | |
| 1268 | ✗ | const int size = int(args[5].AsFloat(24) + 0.5); | |
| 1269 | ✗ | const int font_width = int(args[8].AsFloat(0) + 0.5); | |
| 1270 | ✗ | const bool bold = args[10].AsBool(false); | |
| 1271 | #endif | ||
| 1272 | ✗ | const int text_color = args[6].AsInt(0xFFFF00); | |
| 1273 | ✗ | const int halo_color = args[7].AsInt(0); | |
| 1274 | ✗ | const int font_angle = int(args[9].AsFloat(0)*10+0.5); | |
| 1275 | ✗ | const bool italic = args[11].AsBool(false); | |
| 1276 | ✗ | const bool noaa = args[12].AsBool(false); | |
| 1277 | |||
| 1278 | ✗ | return new ShowSMPTE(clip, 0.0, NULL, offset_f, x, y, font, size, text_color, halo_color, font_width, font_angle, bold, italic, noaa, gdi, env); | |
| 1279 | ✗ | } | |
| 1280 | |||
| 1281 | |||
| 1282 | |||
| 1283 | |||
| 1284 | |||
| 1285 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 1286 | |||
| 1287 | /*********************************** | ||
| 1288 | ******* Subtitle Filter ****** | ||
| 1289 | **********************************/ | ||
| 1290 | |||
| 1291 | Subtitle::Subtitle( PClip _child, const char _text[], int _x, int _y, int _firstframe, | ||
| 1292 | int _lastframe, const char _fontname[], int _size, int _textcolor, | ||
| 1293 | int _halocolor, int _align, int _spc, bool _multiline, int _lsp, | ||
| 1294 | int _font_width, int _font_angle, bool _interlaced, const char _font_filename[], const bool _utf8, | ||
| 1295 | const bool _bold, const bool _italic, const bool _noaa, int _chromaplacement, IScriptEnvironment* env) | ||
| 1296 | : GenericVideoFilter(_child), | ||
| 1297 | x(_x), y(_y), | ||
| 1298 | firstframe(_firstframe), lastframe(_lastframe), size(_size), | ||
| 1299 | lsp(_lsp), font_width(_font_width), font_angle(_font_angle), multiline(_multiline), interlaced(_interlaced), | ||
| 1300 | textcolor(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_textcolor) : _textcolor), | ||
| 1301 | halocolor(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_halocolor) : _halocolor), | ||
| 1302 | align(_align), spc(_spc), | ||
| 1303 | fontname(_fontname), text(_text), font_filename(_font_filename), utf8(_utf8), | ||
| 1304 | bold(_bold), italic(_italic), noaa(_noaa), | ||
| 1305 | // only three types supported | ||
| 1306 | chromaplacement( | ||
| 1307 | _chromaplacement == ChromaLocation_e::AVS_CHROMA_CENTER || | ||
| 1308 | _chromaplacement == ChromaLocation_e::AVS_CHROMA_LEFT || | ||
| 1309 | _chromaplacement == ChromaLocation_e::AVS_CHROMA_TOP_LEFT ? _chromaplacement : ChromaLocation_e::AVS_CHROMA_LEFT), | ||
| 1310 | antialiaser(nullptr) | ||
| 1311 | { | ||
| 1312 | if (*font_filename) { | ||
| 1313 | int added_font_count = AddFontResourceEx( | ||
| 1314 | font_filename, // font file name | ||
| 1315 | FR_PRIVATE, // font characteristics | ||
| 1316 | NULL); | ||
| 1317 | if (added_font_count == 0) | ||
| 1318 | env->ThrowError("SubTitle: font %s not found", font_filename); | ||
| 1319 | } | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | |||
| 1323 | |||
| 1324 | Subtitle::~Subtitle(void) | ||
| 1325 | { | ||
| 1326 | delete antialiaser; | ||
| 1327 | if (font_filename) { | ||
| 1328 | RemoveFontResourceEx( | ||
| 1329 | font_filename, // name of font file | ||
| 1330 | FR_PRIVATE, // font characteristics | ||
| 1331 | NULL // Reserved. | ||
| 1332 | ); | ||
| 1333 | } | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | |||
| 1337 | |||
| 1338 | PVideoFrame Subtitle::GetFrame(int n, IScriptEnvironment* env) | ||
| 1339 | { | ||
| 1340 | PVideoFrame frame = child->GetFrame(n, env); | ||
| 1341 | |||
| 1342 | if (n >= firstframe && n <= lastframe) { | ||
| 1343 | env->MakeWritable(&frame); | ||
| 1344 | if (!antialiaser) // :FIXME: CriticalSection | ||
| 1345 | InitAntialiaser(env); | ||
| 1346 | if (antialiaser) { | ||
| 1347 | antialiaser->Apply(vi, &frame, frame->GetPitch()); | ||
| 1348 | // Release all the windows drawing stuff | ||
| 1349 | // and just keep the alpha calcs | ||
| 1350 | antialiaser->FreeDC(); | ||
| 1351 | } | ||
| 1352 | } | ||
| 1353 | // if we get far enough away from the frames we're supposed to | ||
| 1354 | // subtitle, then junk the buffered drawing information | ||
| 1355 | if (antialiaser && (n < firstframe-10 || n > lastframe+10 || n == vi.num_frames-1)) { | ||
| 1356 | delete antialiaser; | ||
| 1357 | antialiaser = 0; // :FIXME: CriticalSection | ||
| 1358 | } | ||
| 1359 | |||
| 1360 | return frame; | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | AVSValue __cdecl Subtitle::Create(AVSValue args, void*, IScriptEnvironment* env) | ||
| 1364 | { | ||
| 1365 | PClip clip = args[0].AsClip(); | ||
| 1366 | const char* text = args[1].AsString(); | ||
| 1367 | const int first_frame = args[4].AsInt(0); | ||
| 1368 | const int last_frame = args[5].AsInt(clip->GetVideoInfo().num_frames-1); | ||
| 1369 | const char* font = args[6].AsString("Arial"); | ||
| 1370 | const int size = int(args[7].AsFloat(18)*8+0.5); | ||
| 1371 | const int text_color = args[8].AsInt(0xFFFF00); | ||
| 1372 | const int halo_color = args[9].AsInt(0); | ||
| 1373 | const int align = args[10].AsInt(args[2].AsFloat(0)==-1?2:7); | ||
| 1374 | const int spc = args[11].AsInt(0); | ||
| 1375 | const bool multiline = args[12].Defined(); | ||
| 1376 | const int lsp = args[12].AsInt(0); | ||
| 1377 | const int font_width = int(args[13].AsFloat(0)*8+0.5); | ||
| 1378 | const int font_angle = int(args[14].AsFloat(0)*10+0.5); | ||
| 1379 | const bool interlaced = args[15].AsBool(false); | ||
| 1380 | const char* font_filename = args[16].AsString(""); | ||
| 1381 | const bool utf8 = args[17].AsBool(false); | ||
| 1382 | const bool bold = args[18].AsBool(true); | ||
| 1383 | const bool italic = args[19].AsBool(false); | ||
| 1384 | const bool noaa = args[20].AsBool(false); | ||
| 1385 | const char* placement_name = args[21].AsString(nullptr); | ||
| 1386 | const bool gdi = args[22].AsBool(true); | ||
| 1387 | |||
| 1388 | if (!gdi) | ||
| 1389 | return SimpleText::Create(args, nullptr, env); | ||
| 1390 | |||
| 1391 | VideoInfo vi = clip->GetVideoInfo(); | ||
| 1392 | int ChromaLocation_In = -1; | ||
| 1393 | if (vi.IsYV411()) { | ||
| 1394 | auto frame0 = clip->GetFrame(0, env); | ||
| 1395 | const AVSMap* props = env->getFramePropsRO(frame0); | ||
| 1396 | chromaloc_parse_merge_with_props(vi, placement_name, props, ChromaLocation_In, -1, env); | ||
| 1397 | } | ||
| 1398 | else if (vi.Is420() || vi.Is422() || vi.IsYUY2()) { | ||
| 1399 | auto frame0 = clip->GetFrame(0, env); | ||
| 1400 | const AVSMap* props = env->getFramePropsRO(frame0); | ||
| 1401 | chromaloc_parse_merge_with_props(vi, placement_name, props, ChromaLocation_In, ChromaLocation_e::AVS_CHROMA_LEFT, env); | ||
| 1402 | } | ||
| 1403 | |||
| 1404 | if ((align < 1) || (align > 9)) | ||
| 1405 | env->ThrowError("Subtitle: Align values are 1 - 9 mapped to your numeric pad"); | ||
| 1406 | |||
| 1407 | const int subtitle_default_x = 8; | ||
| 1408 | |||
| 1409 | int defx, defy; | ||
| 1410 | bool x_center = false; | ||
| 1411 | bool y_center = false; | ||
| 1412 | |||
| 1413 | switch (align) { | ||
| 1414 | case 1: case 4: case 7: defx = subtitle_default_x; break; | ||
| 1415 | case 2: case 5: case 8: | ||
| 1416 | defx = 0; // n/a if not set later | ||
| 1417 | x_center = true; | ||
| 1418 | break; | ||
| 1419 | case 3: case 6: case 9: defx = clip->GetVideoInfo().width - subtitle_default_x; break; | ||
| 1420 | default: defx = subtitle_default_x; break; | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | switch (align) { | ||
| 1424 | case 1: case 2: case 3: defy = clip->GetVideoInfo().height - 2; break; // bottom alignment 2 pixel above | ||
| 1425 | case 4: case 5: case 6: | ||
| 1426 | defy = 0; // n/a if not set later | ||
| 1427 | y_center = true; | ||
| 1428 | break; | ||
| 1429 | case 7: case 8: case 9: defy = 0; break; | ||
| 1430 | default: defy = (size + 4) / 8; break; | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | const bool isXdefined = args[2].Defined(); | ||
| 1434 | const bool isYdefined = args[3].Defined(); | ||
| 1435 | |||
| 1436 | int x = int(args[2].AsDblDef(defx)*8+0.5); | ||
| 1437 | int y = int(args[3].AsDblDef(defy)*8+0.5); | ||
| 1438 | |||
| 1439 | if (!isXdefined && x_center) | ||
| 1440 | x = (clip->GetVideoInfo().width >> 1) * 8; | ||
| 1441 | |||
| 1442 | if (!isYdefined && y_center) | ||
| 1443 | y = (clip->GetVideoInfo().height >> 1) * 8; | ||
| 1444 | |||
| 1445 | return new Subtitle(clip, text, x, y, first_frame, last_frame, font, size, text_color, | ||
| 1446 | halo_color, align, spc, multiline, lsp, font_width, font_angle, interlaced, font_filename, utf8, | ||
| 1447 | bold, italic, noaa, ChromaLocation_In, env); | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | // multiline separator: string contains '\' and 'n' characters explicitely | ||
| 1451 | // SubTitle compatibility: literal "\n" means line break, but "\\n" means that literal "\n" will be printed | ||
| 1452 | // Since 3.7.4 the C style LF '\n' or CR LF ('\r' and '\n') always results in new line | ||
| 1453 | static std::vector<std::string> SplitLines(const char* text, bool multiline_by_param) { | ||
| 1454 | std::string s = text; | ||
| 1455 | std::vector<std::string> lines; | ||
| 1456 | size_t start = 0; | ||
| 1457 | size_t length = s.length(); | ||
| 1458 | |||
| 1459 | for (size_t i = 0; i < length; ++i) { | ||
| 1460 | |||
| 1461 | if (multiline_by_param && s[i] == '\\') { | ||
| 1462 | if (i + 1 < length && s[i + 1] == 'n') { | ||
| 1463 | if (i > 0 && s[i - 1] == '\\') { | ||
| 1464 | // "\\n" case: keep as literal "\n" | ||
| 1465 | i++; | ||
| 1466 | } | ||
| 1467 | else { | ||
| 1468 | // "\n" case: split the line | ||
| 1469 | lines.push_back(s.substr(start, i - start)); | ||
| 1470 | start = i + 2; | ||
| 1471 | i++; | ||
| 1472 | } | ||
| 1473 | } | ||
| 1474 | } | ||
| 1475 | else if (s[i] == '\n') { | ||
| 1476 | // Usual line feed '\n' case: split the line | ||
| 1477 | lines.push_back(s.substr(start, i - start)); | ||
| 1478 | start = i + 1; | ||
| 1479 | } | ||
| 1480 | else if (s[i] == '\r' && i + 1 < length && s[i + 1] == '\n') { | ||
| 1481 | // Windows-style CR LF case: split the line | ||
| 1482 | lines.push_back(s.substr(start, i - start)); | ||
| 1483 | start = i + 2; | ||
| 1484 | i++; | ||
| 1485 | } | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | // Add the last line if it's not empty | ||
| 1489 | if (start < length) { | ||
| 1490 | lines.push_back(s.substr(start)); | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | return lines; | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | static int CorrectYbyTextAndAlignment(int real_y, int align, int fontsize, int lsp, size_t line_count) | ||
| 1497 | { | ||
| 1498 | // note: we do not really have a vertical center alignment for multiline strings since it means not centering. | ||
| 1499 | // TA_BASELINE is aligning (x,y) to the character's baseline: letters like g, y and q would reach below that baseline) | ||
| 1500 | |||
| 1501 | // when multiline, bottom and vertically centered cases affect starting y | ||
| 1502 | if (align == 1 || align == 2 || align == 3) // bottom | ||
| 1503 | real_y -= (fontsize + lsp) * ((int)line_count - 1); | ||
| 1504 | else if (align == 4 || align == 5 || align == 6) | ||
| 1505 | real_y -= ((fontsize + lsp) * ((int)line_count - 1) + 1) / 2; | ||
| 1506 | return real_y; | ||
| 1507 | } | ||
| 1508 | |||
| 1509 | void Subtitle::InitAntialiaser(IScriptEnvironment* env) | ||
| 1510 | { | ||
| 1511 | antialiaser = new Antialiaser(vi.width, vi.height, fontname, size, textcolor, halocolor, bold, italic, noaa, | ||
| 1512 | env->GetCPUFlagsEx(), chromaplacement, | ||
| 1513 | font_width, font_angle, interlaced); | ||
| 1514 | |||
| 1515 | int real_x = x; | ||
| 1516 | int real_y = y; | ||
| 1517 | unsigned int al = 0; | ||
| 1518 | std::vector<std::string> lines; | ||
| 1519 | |||
| 1520 | HDC hdcAntialias = antialiaser->GetDC(); | ||
| 1521 | if (!hdcAntialias) goto GDIError; | ||
| 1522 | |||
| 1523 | switch (align) // This spec where [X, Y] is relative to the text (inverted logic) | ||
| 1524 | { case 1: al = TA_BOTTOM | TA_LEFT; break; // .---- | ||
| 1525 | case 2: al = TA_BOTTOM | TA_CENTER; break; // --.-- | ||
| 1526 | case 3: al = TA_BOTTOM | TA_RIGHT; break; // ----. | ||
| 1527 | case 4: al = TA_BASELINE | TA_LEFT; break; // .____ | ||
| 1528 | case 5: al = TA_BASELINE | TA_CENTER; break; // __.__ | ||
| 1529 | case 6: al = TA_BASELINE | TA_RIGHT; break; // ____. | ||
| 1530 | case 7: al = TA_TOP | TA_LEFT; break; // `---- | ||
| 1531 | case 8: al = TA_TOP | TA_CENTER; break; // --`-- | ||
| 1532 | case 9: al = TA_TOP | TA_RIGHT; break; // ----` | ||
| 1533 | default: al= TA_BASELINE | TA_LEFT; break; // .____ | ||
| 1534 | } | ||
| 1535 | if (SetTextCharacterExtra(hdcAntialias, spc) == 0x80000000) goto GDIError; | ||
| 1536 | if (SetTextAlign(hdcAntialias, al) == GDI_ERROR) goto GDIError; | ||
| 1537 | |||
| 1538 | // multiline is filter parameter, true when lsp is given. | ||
| 1539 | // 3.7.4 real LF or CR LF breaks line unconditionally. | ||
| 1540 | lines = SplitLines(text, multiline); | ||
| 1541 | if (lines.size() == 0) return; | ||
| 1542 | real_y = CorrectYbyTextAndAlignment(real_y, align, size, lsp, lines.size()); // find literal \ n for LF, not real LF 0x0A | ||
| 1543 | |||
| 1544 | if (utf8) { | ||
| 1545 | // Test: | ||
| 1546 | // Title="Cherry blossom "+CHR($E6)+CHR($A1)+CHR($9C)+CHR($E3)+CHR($81)+CHR($AE)+CHR($E8)+CHR($8A)+CHR($B1) | ||
| 1547 | // SubTitle(Title, utf8 = true) | ||
| 1548 | int y_inc = real_y + 16; | ||
| 1549 | for(auto s : lines) { | ||
| 1550 | auto textw = Utf8ToWideChar(s.c_str()); | ||
| 1551 | if (!TextOutW(hdcAntialias, real_x + 16, y_inc, textw.get(), (int)wcslen(textw.get()))) | ||
| 1552 | goto GDIError; | ||
| 1553 | y_inc += size + lsp; | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | } | ||
| 1557 | else { | ||
| 1558 | int y_inc = real_y + 16; | ||
| 1559 | for (auto s : lines) { | ||
| 1560 | if (!TextOut(hdcAntialias, real_x + 16, y_inc, s.c_str(), (int)s.length())) | ||
| 1561 | goto GDIError; | ||
| 1562 | y_inc += size + lsp; | ||
| 1563 | } | ||
| 1564 | } | ||
| 1565 | if (!GdiFlush()) goto GDIError; | ||
| 1566 | return; | ||
| 1567 | |||
| 1568 | GDIError: | ||
| 1569 | delete antialiaser; | ||
| 1570 | antialiaser = 0; | ||
| 1571 | |||
| 1572 | env->ThrowError("Subtitle: GDI or Insufficient Memory Error"); | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | |||
| 1576 | |||
| 1577 | #endif | ||
| 1578 | |||
| 1579 | ✗ | static int CalcFontSizeForInfo(int w, int h, bool autolarge, int upper_limit, bool gdi) | |
| 1580 | { | ||
| 1581 | // if frame is smaller than minimum then font size will be decreased proportionally | ||
| 1582 | // normalized to 640x480 @fontsize=15: | ||
| 1583 | // 14 lines @height=15 takes ~480/2 pixels (half vertical screens) | ||
| 1584 | // Info screen takes horizontally ~0.6 * 640 pixels at font size 15 | ||
| 1585 | |||
| 1586 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 1587 | const int min_font_size = gdi ? 1 : 12; | ||
| 1588 | const int max_font_size = gdi ? 255 : 32; | ||
| 1589 | const int reference_font_size = gdi ? 15 : 18; | ||
| 1590 | const int reference_font_width = gdi ? 8 : 10; | ||
| 1591 | #else | ||
| 1592 | AVS_UNUSED(gdi); | ||
| 1593 | ✗ | const int min_font_size = 12; | |
| 1594 | ✗ | const int max_font_size = 32; | |
| 1595 | ✗ | const int reference_font_size = 18; // 16x8 or 18x10, latter is more visible | |
| 1596 | ✗ | const int reference_font_width = 10; | |
| 1597 | #endif | ||
| 1598 | // knowing that a usual Info() is 45x15 characters, plus a margin | ||
| 1599 | ✗ | const int reference_min_height = 15 * reference_font_size; | |
| 1600 | ✗ | const int reference_min_width = 48 * reference_font_width; // (45 + margin) characters | |
| 1601 | |||
| 1602 | // 0..reference_min_width :decrease (as always in Avs) | ||
| 1603 | // reference_min_width ... 640 : constant size | ||
| 1604 | // 640+ : enlarge when autolarge is enabled or else constant size | ||
| 1605 | |||
| 1606 | int w_restricted_font_size; | ||
| 1607 | ✗ | if (w < reference_min_width) | |
| 1608 | ✗ | w_restricted_font_size = (reference_font_size * w) / reference_min_width; | |
| 1609 | ✗ | else if (w >= 640 && autolarge) | |
| 1610 | ✗ | w_restricted_font_size = reference_font_size * w / 640; | |
| 1611 | else | ||
| 1612 | ✗ | w_restricted_font_size = reference_font_size; | |
| 1613 | |||
| 1614 | int h_restricted_font_size; | ||
| 1615 | ✗ | if (h < reference_min_height) | |
| 1616 | ✗ | h_restricted_font_size = (reference_font_size * h) / reference_min_height; | |
| 1617 | ✗ | else if (h >= 480 && autolarge) | |
| 1618 | ✗ | h_restricted_font_size = reference_font_size * h / 480; | |
| 1619 | else | ||
| 1620 | ✗ | h_restricted_font_size = reference_font_size; | |
| 1621 | |||
| 1622 | ✗ | int effective_font_size = (w_restricted_font_size < h_restricted_font_size) ? w_restricted_font_size : h_restricted_font_size; | |
| 1623 | |||
| 1624 | ✗ | effective_font_size = std::max(effective_font_size, min_font_size); | |
| 1625 | ✗ | if (upper_limit > 0) | |
| 1626 | ✗ | effective_font_size = std::min(effective_font_size, max_font_size); | |
| 1627 | |||
| 1628 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 1629 | return gdi ? effective_font_size * 8 : effective_font_size / 2 * 2; | ||
| 1630 | #else | ||
| 1631 | ✗ | return effective_font_size / 2 * 2; // no odd sized fixed fonts atm. | |
| 1632 | #endif | ||
| 1633 | |||
| 1634 | } | ||
| 1635 | |||
| 1636 | /*********************************** | ||
| 1637 | ******* SimpleText Filter ***** | ||
| 1638 | ***********************************/ | ||
| 1639 | |||
| 1640 | ✗ | SimpleText::SimpleText(PClip _child, const char _text[], int _x, int _y, int _firstframe, | |
| 1641 | int _lastframe, const char _fontname[], int _size, int _textcolor, | ||
| 1642 | int _halocolor, int _align, int _spc, bool _multiline, int _lsp, | ||
| 1643 | int _font_width, int _font_angle, bool _interlaced, const char _font_filename[], | ||
| 1644 | const bool _utf8, const bool _bold, const int _chromalocation, | ||
| 1645 | ✗ | IScriptEnvironment* env) | |
| 1646 | : GenericVideoFilter(_child), | ||
| 1647 | ✗ | x(_x), y(_y), | |
| 1648 | ✗ | firstframe(_firstframe), lastframe(_lastframe), size(_size), lsp(_lsp), | |
| 1649 | ✗ | multiline(_multiline), | |
| 1650 | ✗ | textcolor(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_textcolor) : _textcolor), | |
| 1651 | ✗ | halocolor(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_halocolor) : _halocolor), // not supported | |
| 1652 | ✗ | align(_align), | |
| 1653 | ✗ | halocolor_orig(_halocolor), | |
| 1654 | ✗ | fontname(_fontname), | |
| 1655 | ✗ | text(_text), | |
| 1656 | // spc(_spc), font_width(_font_width), font_angle(_font_angle), interlaced(_interlaced), | ||
| 1657 | ✗ | font_filename(_font_filename), utf8(_utf8), | |
| 1658 | ✗ | bold(_bold), | |
| 1659 | ✗ | chromalocation(_chromalocation) | |
| 1660 | { | ||
| 1661 | |||
| 1662 | ✗ | if (*font_filename) { | |
| 1663 | // external font file | ||
| 1664 | ✗ | bool debugSave = size < 0; | |
| 1665 | ✗ | current_font = GetBitmapFont(0, font_filename, false, debugSave); // 0: size n/a | |
| 1666 | ✗ | if (current_font == nullptr) | |
| 1667 | ✗ | env->ThrowError("SimpleText: file %s not found or unknown file format", font_filename); | |
| 1668 | } | ||
| 1669 | else | ||
| 1670 | { | ||
| 1671 | // internal font | ||
| 1672 | ✗ | if (fontname) { | |
| 1673 | ✗ | current_font = GetBitmapFont(size, fontname, bold, false); // 12, "Terminus" | |
| 1674 | ✗ | if (current_font == nullptr) | |
| 1675 | ✗ | env->ThrowError("SimpleText: internal font name %s in size %d not found", fontname, size); | |
| 1676 | } | ||
| 1677 | else { | ||
| 1678 | // size | ||
| 1679 | ✗ | current_font = GetBitmapFont(size, "", bold, false); // 12, "" | |
| 1680 | ✗ | if (current_font == nullptr) | |
| 1681 | ✗ | env->ThrowError("SimpleText: fixed font size %d not found", size); | |
| 1682 | } | ||
| 1683 | } | ||
| 1684 | ✗ | } | |
| 1685 | |||
| 1686 | |||
| 1687 | |||
| 1688 | ✗ | SimpleText::~SimpleText(void) | |
| 1689 | { | ||
| 1690 | // nothing here yet | ||
| 1691 | ✗ | } | |
| 1692 | |||
| 1693 | |||
| 1694 | ✗ | PVideoFrame SimpleText::GetFrame(int n, IScriptEnvironment* env) | |
| 1695 | { | ||
| 1696 | ✗ | PVideoFrame frame = child->GetFrame(n, env); | |
| 1697 | |||
| 1698 | ✗ | if (n >= firstframe && n <= lastframe) { | |
| 1699 | ✗ | env->MakeWritable(&frame); | |
| 1700 | |||
| 1701 | ✗ | int real_x = x; | |
| 1702 | ✗ | int real_y = y; | |
| 1703 | |||
| 1704 | // Test: | ||
| 1705 | // Title="Cherry blossom "+CHR($E6)+CHR($A1)+CHR($9C)+CHR($E3)+CHR($81)+CHR($AE)+CHR($E8)+CHR($8A)+CHR($B1) | ||
| 1706 | ✗ | std::string s(text); | |
| 1707 | |||
| 1708 | ✗ | if (multiline) { // filter parameter, true when lsp is given | |
| 1709 | // multiline case: string contains '\' and 'n' characters explicitely | ||
| 1710 | // SubTitle compatibility: literal "\n" means line break, but "\\n" means that literal "\n" will be printed | ||
| 1711 | // Thus we replace two-character literal "\n" to \n (0x0A) then when "\" and \n found, we change it back to literal "\n" | ||
| 1712 | ✗ | size_t index = 0; | |
| 1713 | while (true) { | ||
| 1714 | ✗ | index = s.find("\\n", index); | |
| 1715 | ✗ | if (index == std::string::npos) break; | |
| 1716 | ✗ | s.replace(index, 1, "\n"); | |
| 1717 | ✗ | s.erase(index + 1, 1); // two characters replaced by a single one, erase at the second position | |
| 1718 | ✗ | index += 1; // length of the string to replace | |
| 1719 | } | ||
| 1720 | // '\' and '\n' back to literal "\n" | ||
| 1721 | ✗ | index = 0; | |
| 1722 | while (true) { | ||
| 1723 | ✗ | index = s.find("\\\n", index); // yes, \ and \n | |
| 1724 | ✗ | if (index == std::string::npos) break; | |
| 1725 | ✗ | s.replace(index, 2, "\\n"); // back to "\" + "n" | |
| 1726 | ✗ | index += 2; // length of the string to replace | |
| 1727 | } | ||
| 1728 | } | ||
| 1729 | |||
| 1730 | ✗ | std::string s_utf8 = charToUtf8(s.c_str(), utf8); // to wchar_t either from utf8 (win/linux) or ansi (win) | |
| 1731 | |||
| 1732 | // halocolor MSB | ||
| 1733 | // FF: fadeIt, no halo | ||
| 1734 | // FE: fadeIt, use halocolor | ||
| 1735 | // 01-FD: no halo | ||
| 1736 | // 00: use halocolor | ||
| 1737 | ✗ | int halocolor_msb = (halocolor_orig & 0xFF000000) >> 24; | |
| 1738 | ✗ | SimpleTextOutW_multi(current_font.get(), vi, frame, real_x, real_y, s_utf8, | |
| 1739 | ✗ | halocolor_msb == 0xFF || halocolor_msb == 0xFE, // fadeIt, special halocolor, when MSB byte is FF or FE | |
| 1740 | ✗ | textcolor, halocolor, | |
| 1741 | ✗ | halocolor_msb == 0x00 || halocolor_msb == 0xFE, // use halocolor when MSB byte is 00 or FE | |
| 1742 | ✗ | align, lsp, chromalocation); | |
| 1743 | ✗ | } | |
| 1744 | |||
| 1745 | ✗ | return frame; | |
| 1746 | ✗ | } | |
| 1747 | |||
| 1748 | ✗ | AVSValue __cdecl SimpleText::Create(AVSValue args, void*, IScriptEnvironment* env) | |
| 1749 | { | ||
| 1750 | ✗ | PClip clip = args[0].AsClip(); | |
| 1751 | ✗ | VideoInfo vi = clip->GetVideoInfo(); | |
| 1752 | |||
| 1753 | ✗ | const char* text = args[1].AsString(); | |
| 1754 | ✗ | const int first_frame = args[4].AsInt(0); | |
| 1755 | ✗ | const int last_frame = args[5].AsInt(vi.num_frames - 1); | |
| 1756 | ✗ | const char* font = args[6].AsString("Terminus"); // Terminus, info_h are embedded | |
| 1757 | ✗ | const int size = int(args[7].AsFloat(18)); // height 12, 14, 16, 18, 20, 24, 28, 32 | |
| 1758 | ✗ | const int text_color = args[8].AsInt(0xFFFF00); | |
| 1759 | ✗ | const int halo_color = args[9].AsInt(0); | |
| 1760 | // Warning: if x=-1 passed + no align specified: sets bottom center alignment. | ||
| 1761 | // This is why: SubTitle definition: Can be set to -1 to automatically center the text horizontally or vertically, respectively. | ||
| 1762 | // Negative values of x and y not equal to -1 can be used to move subtitles partially off the screen. | ||
| 1763 | // Contrary to the documentation, y=-1 is not checked at all | ||
| 1764 | ✗ | const int align = args[10].AsInt(args[2].AsFloat(0) == -1 ? 2 : 7); | |
| 1765 | ✗ | const int spc = args[11].AsInt(0); | |
| 1766 | ✗ | const bool multiline = args[12].Defined(); | |
| 1767 | ✗ | const int lsp = args[12].AsInt(0); // line spacing if multiline | |
| 1768 | ✗ | const int font_width = int(args[13].AsFloat(0) * 8 + 0.5); // n/a | |
| 1769 | ✗ | const int font_angle = int(args[14].AsFloat(0) * 10 + 0.5); // n/a | |
| 1770 | ✗ | const bool interlaced = args[15].AsBool(false); // n/a | |
| 1771 | ✗ | const char* font_filename = args[16].AsString(""); | |
| 1772 | ✗ | const bool utf8 = args[17].AsBool(false); // linux: n/a | |
| 1773 | ✗ | const bool bold = args[18].AsBool(false); // valid SubTitle parameter since v3.7.3 | |
| 1774 | ✗ | [[maybe_unused]] const bool italic = args[19].AsBool(false); // valid SubTitle parameter since v3.7.3, but in "Text" not implemented | |
| 1775 | ✗ | [[maybe_unused]] const bool noaa = args[20].AsBool(false); // valid SubTitle parameter since v3.7.3, but in "Text" not implemented | |
| 1776 | // "placement" at [21], "gdi" at [22] — identical for both "Text" and "Subtitle" no-GDI fallback | ||
| 1777 | ✗ | const char* placement_name = args.ArraySize() >= 22 ? args[21].AsString(nullptr) : nullptr; | |
| 1778 | ✗ | [[maybe_unused]] const bool gdi = args.ArraySize() >= 23 ? args[22].AsBool(false) : false; // ignored by SimpleText | |
| 1779 | |||
| 1780 | // parameters marked with n/a are ignored; parameter list is currently the same as SubTitle | ||
| 1781 | |||
| 1782 | ✗ | if ((align < 1) || (align > 9)) | |
| 1783 | ✗ | env->ThrowError("SimpleText: Align values are 1 - 9 mapped to your numeric pad"); | |
| 1784 | |||
| 1785 | int defx, defy; | ||
| 1786 | ✗ | bool x_center = false; | |
| 1787 | ✗ | bool y_center = false; | |
| 1788 | |||
| 1789 | ✗ | switch (align) { | |
| 1790 | ✗ | case 1: case 4: case 7: defx = 8; break; | |
| 1791 | ✗ | case 2: case 5: case 8: | |
| 1792 | ✗ | defx = 0; // n/a if not set later | |
| 1793 | ✗ | x_center = true; | |
| 1794 | ✗ | break; | |
| 1795 | ✗ | case 3: case 6: case 9: defx = clip->GetVideoInfo().width - 8; break; | |
| 1796 | ✗ | default: defx = 8; break; | |
| 1797 | } | ||
| 1798 | ✗ | switch (align) { | |
| 1799 | ✗ | case 1: case 2: case 3: defy = clip->GetVideoInfo().height - 2; break; | |
| 1800 | ✗ | case 4: case 5: case 6: | |
| 1801 | ✗ | defy = 0; // n/a if not set later | |
| 1802 | ✗ | y_center = true; | |
| 1803 | ✗ | break; | |
| 1804 | ✗ | case 7: case 8: case 9: defy = 0; break; | |
| 1805 | ✗ | default: defy = /*(size + 4) / 8;*/ (size + 1) / 2; break; // no mul 8 | |
| 1806 | } | ||
| 1807 | |||
| 1808 | ✗ | const bool isXdefined = args[2].Defined(); | |
| 1809 | ✗ | const bool isYdefined = args[3].Defined(); | |
| 1810 | // prevent turning exactly given x or y == -1 into 0, use lround instead of int cast. int(-1 + 0.5) --> 0 :( | ||
| 1811 | ✗ | const int x = std::lround(args[2].AsDblDef(defx)); | |
| 1812 | ✗ | const int y = std::lround(args[3].AsDblDef(defy)); | |
| 1813 | |||
| 1814 | ✗ | int real_x = x; | |
| 1815 | ✗ | int real_y = y; | |
| 1816 | |||
| 1817 | // center check | ||
| 1818 | ✗ | if (!isXdefined && x_center) | |
| 1819 | ✗ | real_x = (clip->GetVideoInfo().width >> 1) /* * 8 */; // no mul 8 like in SubTitle | |
| 1820 | |||
| 1821 | ✗ | if (!isYdefined && y_center) | |
| 1822 | ✗ | real_y = (clip->GetVideoInfo().height >> 1) /* * 8 */; // no mul 8 like in SubTitle | |
| 1823 | |||
| 1824 | // anyway, we accept any chroma location here. | ||
| 1825 | // "Text" filter will ignore invalid/not used definitions and use its defaults | ||
| 1826 | ✗ | int ChromaLocation_In = -1; // invalid | |
| 1827 | |||
| 1828 | ✗ | if (vi.IsYV411()) { | |
| 1829 | // placement parameter exists, (default none/-1) + input frame properties; 'left'-ish _ChromaLocation is allowed, checked later | ||
| 1830 | ✗ | auto frame0 = clip->GetFrame(0, env); | |
| 1831 | ✗ | const AVSMap* props = env->getFramePropsRO(frame0); | |
| 1832 | ✗ | chromaloc_parse_merge_with_props(vi, placement_name, props, /* ref*/ChromaLocation_In, -1 /*default none chromaloc */, env); | |
| 1833 | ✗ | } | |
| 1834 | ✗ | else if (vi.Is420() || vi.Is422() || vi.IsYUY2()) { | |
| 1835 | // placement parameter is valid + input frame properties | ||
| 1836 | ✗ | auto frame0 = clip->GetFrame(0, env); | |
| 1837 | ✗ | const AVSMap* props = env->getFramePropsRO(frame0); | |
| 1838 | ✗ | chromaloc_parse_merge_with_props(vi, placement_name, props, /* ref*/ChromaLocation_In, ChromaLocation_e::AVS_CHROMA_LEFT /*default*/, env); | |
| 1839 | ✗ | } | |
| 1840 | |||
| 1841 | return new SimpleText(clip, text, real_x, real_y, first_frame, last_frame, font, size, text_color, | ||
| 1842 | ✗ | halo_color, align, spc, multiline, lsp, font_width, font_angle, interlaced, font_filename, utf8, bold, ChromaLocation_In, env); | |
| 1843 | ✗ | } | |
| 1844 | |||
| 1845 | |||
| 1846 | |||
| 1847 | /*********************************** | ||
| 1848 | ******* FilterInfo Filter ****** | ||
| 1849 | **********************************/ | ||
| 1850 | |||
| 1851 | ✗ | FilterInfo::FilterInfo( PClip _child, const char _fontname[], int _size, int _textcolor, int _halocolor, bool _bold, bool _italic, bool _noaa, | |
| 1852 | ✗ | bool _cpu, int _x, int _y, int _align, bool _gdi, IScriptEnvironment* env) | |
| 1853 | ✗ | : GenericVideoFilter(_child), vii(AdjustVi()), size(_size), | |
| 1854 | ✗ | text_color(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_textcolor) : _textcolor), | |
| 1855 | ✗ | halo_color(vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_halocolor) : _halocolor), | |
| 1856 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 1857 | use_gdi(_gdi), | ||
| 1858 | antialiaser(_gdi ? std::make_unique<Antialiaser>(vi.width, vi.height, _fontname, size, | ||
| 1859 | vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_textcolor) : _textcolor, | ||
| 1860 | vi.IsYUV() || vi.IsYUVA() ? RGB2YUV_Rec601(_halocolor) : _halocolor, | ||
| 1861 | _bold, _italic, _noaa, | ||
| 1862 | env->GetCPUFlagsEx(), ChromaLocation_e::AVS_CHROMA_CENTER, /* quick */ | ||
| 1863 | 0, 0, false) : nullptr), | ||
| 1864 | #else | ||
| 1865 | ✗ | use_gdi(false), | |
| 1866 | #endif | ||
| 1867 | ✗ | bold(_bold), italic(_italic), noaa(_noaa), | |
| 1868 | ✗ | cpu(_cpu), x(_x), y(_y), align(_align) | |
| 1869 | { | ||
| 1870 | AVS_UNUSED(env); | ||
| 1871 | |||
| 1872 | ✗ | if (!use_gdi) { | |
| 1873 | ✗ | chromaplacement = ChromaLocation_e::AVS_CHROMA_LEFT; | |
| 1874 | ✗ | current_font = GetBitmapFont(size, "Terminus", bold, false); | |
| 1875 | ✗ | if (current_font == nullptr) { | |
| 1876 | ✗ | current_font = GetBitmapFont(size, "", bold, false); | |
| 1877 | ✗ | if (current_font == nullptr) | |
| 1878 | ✗ | current_font = GetBitmapFont(size, "", !bold, false); | |
| 1879 | } | ||
| 1880 | } | ||
| 1881 | ✗ | } | |
| 1882 | |||
| 1883 | |||
| 1884 | ✗ | FilterInfo::~FilterInfo(void) | |
| 1885 | { | ||
| 1886 | ✗ | } | |
| 1887 | |||
| 1888 | |||
| 1889 | ✗ | const VideoInfo& FilterInfo::AdjustVi() | |
| 1890 | { | ||
| 1891 | ✗ | if ( !vi.HasVideo() ) { | |
| 1892 | ✗ | vi.fps_denominator=1; | |
| 1893 | ✗ | vi.fps_numerator=24; | |
| 1894 | ✗ | vi.height=480; | |
| 1895 | ✗ | vi.num_frames=240; | |
| 1896 | ✗ | vi.pixel_type=VideoInfo::CS_BGR32; | |
| 1897 | ✗ | vi.width=640; | |
| 1898 | ✗ | vi.SetFieldBased(false); | |
| 1899 | } | ||
| 1900 | ✗ | return child->GetVideoInfo(); | |
| 1901 | } | ||
| 1902 | |||
| 1903 | |||
| 1904 | const char* const t_INT8="Integer 8 bit"; | ||
| 1905 | const char* const t_INT16="Integer 16 bit"; | ||
| 1906 | const char* const t_INT24="Integer 24 bit"; | ||
| 1907 | const char* const t_INT32="Integer 32 bit"; | ||
| 1908 | const char* const t_FLOAT32="Float 32 bit"; | ||
| 1909 | const char* const t_YES="YES"; | ||
| 1910 | const char* const t_NO="NO"; | ||
| 1911 | const char* const t_NONE="NONE"; | ||
| 1912 | const char* const t_TFF ="Top Field First "; | ||
| 1913 | const char* const t_BFF ="Bottom Field First "; | ||
| 1914 | const char* const t_ATFF="Assumed Top Field First "; | ||
| 1915 | const char* const t_ABFF="Assumed Bottom Field First "; | ||
| 1916 | const char* const t_STFF="Top Field (Separated) "; | ||
| 1917 | const char* const t_SBFF="Bottom Field (Separated) "; | ||
| 1918 | |||
| 1919 | ✗ | static std::string GetCacheInfo(IScriptEnvironment* env) | |
| 1920 | { | ||
| 1921 | ✗ | std::stringstream ss; | |
| 1922 | ✗ | size_t l2_cache_size = env->GetEnvProperty(AEP_CACHESIZE_L2); | |
| 1923 | ✗ | if (l2_cache_size > 0) | |
| 1924 | ✗ | ss << "L2 Cache Size: " << l2_cache_size << " bytes"; | |
| 1925 | ✗ | return ss.str(); | |
| 1926 | ✗ | } | |
| 1927 | |||
| 1928 | #ifdef INTEL_INTRINSICS | ||
| 1929 | ✗ | std::string GetCpuMsg(IScriptEnvironment * env, bool avx512) | |
| 1930 | { | ||
| 1931 | ✗ | int64_t flags = env->GetCPUFlagsEx(); | |
| 1932 | ✗ | std::stringstream ss; | |
| 1933 | |||
| 1934 | ✗ | if (!avx512) { | |
| 1935 | #ifndef _M_X64 | ||
| 1936 | // don't display old capabilities when at least AVX is used | ||
| 1937 | ✗ | if (!(flags & CPUF_AVX)) { | |
| 1938 | //if (flags & CPUF_FPU) | ||
| 1939 | // ss << "x87 "; | ||
| 1940 | ✗ | if (flags & CPUF_MMX) | |
| 1941 | ✗ | ss << "MMX "; | |
| 1942 | ✗ | if (flags & CPUF_INTEGER_SSE) | |
| 1943 | ✗ | ss << "ISSE "; | |
| 1944 | |||
| 1945 | ✗ | if (flags & CPUF_3DNOW_EXT) | |
| 1946 | ✗ | ss << "3DNOW_EXT"; | |
| 1947 | ✗ | else if (flags & CPUF_3DNOW) | |
| 1948 | ✗ | ss << "3DNOW "; | |
| 1949 | } | ||
| 1950 | |||
| 1951 | ✗ | if (flags & CPUF_SSE) | |
| 1952 | ✗ | ss << "SSE "; | |
| 1953 | #endif | ||
| 1954 | ✗ | if (flags & CPUF_SSE2) | |
| 1955 | ✗ | ss << "SSE2 "; | |
| 1956 | ✗ | if (flags & CPUF_SSE3) | |
| 1957 | ✗ | ss << "SSE3 "; | |
| 1958 | ✗ | if (flags & CPUF_SSSE3) | |
| 1959 | ✗ | ss << "SSSE3 "; | |
| 1960 | ✗ | if (flags & CPUF_SSE4_1) | |
| 1961 | ✗ | ss << "SSE4.1 "; | |
| 1962 | ✗ | if (flags & CPUF_SSE4_2) | |
| 1963 | ✗ | ss << "SSE4.2 "; | |
| 1964 | |||
| 1965 | ✗ | if (flags & CPUF_AVX) | |
| 1966 | ✗ | ss << "AVX "; | |
| 1967 | ✗ | if (flags & CPUF_AVX2) | |
| 1968 | ✗ | ss << "AVX2 "; | |
| 1969 | ✗ | if (flags & CPUF_FMA3) | |
| 1970 | ✗ | ss << "FMA3 "; | |
| 1971 | ✗ | if (flags & CPUF_FMA4) | |
| 1972 | ✗ | ss << "FMA4 "; | |
| 1973 | ✗ | if (flags & CPUF_F16C) | |
| 1974 | ✗ | ss << "F16C "; | |
| 1975 | } | ||
| 1976 | else { | ||
| 1977 | |||
| 1978 | ✗ | if (flags & CPUF_AVX512_MASK) | |
| 1979 | ✗ | ss << "AVX512 "; | |
| 1980 | // Core AVX-512 Extensions as a distinct flag (F, CD, BW, DQ, VL) | ||
| 1981 | ✗ | if (flags & CPUF_AVX512_BASE) | |
| 1982 | ✗ | ss << "BASE ("; | |
| 1983 | |||
| 1984 | // Base part (F, CD, BW, DQ, VL) | ||
| 1985 | ✗ | if (flags & CPUF_AVX512F) ss << "F "; | |
| 1986 | ✗ | if (flags & CPUF_AVX512CD) ss << "CD "; | |
| 1987 | ✗ | if (flags & CPUF_AVX512BW) ss << "BW "; | |
| 1988 | ✗ | if (flags & CPUF_AVX512DQ) ss << "DQ "; | |
| 1989 | ✗ | if (flags & CPUF_AVX512VL) { | |
| 1990 | ✗ | if (flags & CPUF_AVX512_BASE) | |
| 1991 | ✗ | ss << "VL) "; | |
| 1992 | else | ||
| 1993 | ✗ | ss << "VL "; | |
| 1994 | } | ||
| 1995 | |||
| 1996 | // ICL part as a distinct flag: | ||
| 1997 | ✗ | if (flags & CPUF_AVX512_FAST) // Ice Lake/Rocket Lake extensions; usable avx-512 | |
| 1998 | ✗ | ss << "FAST ("; | |
| 1999 | ✗ | if (flags & CPUF_AVX512VNNI) ss << "VNNI "; | |
| 2000 | ✗ | if (flags & CPUF_AVX512VBMI) ss << "VBMI "; | |
| 2001 | ✗ | if (flags & CPUF_AVX512VBMI2) ss << "VBMI2 "; | |
| 2002 | ✗ | if (flags & CPUF_AVX512BITALG) ss << "BITALG "; | |
| 2003 | ✗ | if (flags & CPUF_AVX512VPOPCNTDQ) { | |
| 2004 | ✗ | if (flags & CPUF_AVX512_FAST) | |
| 2005 | ✗ | ss << "VPOPCNTDQ) "; | |
| 2006 | else | ||
| 2007 | ✗ | ss << "VPOPCNTDQ "; | |
| 2008 | } | ||
| 2009 | |||
| 2010 | // rest of AVX-512 extensions: | ||
| 2011 | ✗ | if (flags & CPUF_AVX512IFMA) ss << "IFMA "; | |
| 2012 | ✗ | if (flags & CPUF_AVX512BF16) ss << "BF16 "; | |
| 2013 | ✗ | if (flags & CPUF_AVX512FP16) ss << "FP16 "; | |
| 2014 | ✗ | if (flags & CPUF_AVX512PF) ss << "PF "; | |
| 2015 | ✗ | if (flags & CPUF_AVX512ER) ss << "ER "; | |
| 2016 | |||
| 2017 | // Crypto (VAES, VPCLMULQDQ, GFNI) and deprecated (VP2INTERSECT, 4VNNIW, 4FMAPS) extensions excluded | ||
| 2018 | } | ||
| 2019 | ✗ | return ss.str(); | |
| 2020 | ✗ | } | |
| 2021 | #elif defined(ARM64) | ||
| 2022 | // aarch64 ARMv8-A flags | ||
| 2023 | std::string GetCpuMsg(IScriptEnvironment* env) | ||
| 2024 | { | ||
| 2025 | int64_t flags = env->GetCPUFlagsEx(); | ||
| 2026 | std::stringstream ss; | ||
| 2027 | |||
| 2028 | // Tier 1: CPUF_ARM_NEON (Baseline) | ||
| 2029 | if (flags & CPUF_ARM_NEON) | ||
| 2030 | ss << "NEON "; | ||
| 2031 | |||
| 2032 | if (flags & CPUF_ARM_DOTPROD) | ||
| 2033 | ss << "DOTPROD "; | ||
| 2034 | |||
| 2035 | if (flags & CPUF_ARM_I8MM) | ||
| 2036 | ss << "I8MM "; | ||
| 2037 | |||
| 2038 | if (flags & CPUF_ARM_SVE2) | ||
| 2039 | ss << "SVE2 "; | ||
| 2040 | |||
| 2041 | if (flags & CPUF_ARM_SVE2_1) | ||
| 2042 | ss << "SVE2.1 "; | ||
| 2043 | |||
| 2044 | return ss.str(); | ||
| 2045 | } | ||
| 2046 | #else | ||
| 2047 | std::string GetCpuMsg(IScriptEnvironment * env) | ||
| 2048 | { | ||
| 2049 | std::stringstream ss; | ||
| 2050 | |||
| 2051 | return ss.str(); | ||
| 2052 | } | ||
| 2053 | #endif | ||
| 2054 | |||
| 2055 | ✗ | bool FilterInfo::GetParity(int n) | |
| 2056 | { | ||
| 2057 | ✗ | return vii.HasVideo() ? child->GetParity(n) : false; | |
| 2058 | } | ||
| 2059 | |||
| 2060 | |||
| 2061 | ✗ | PVideoFrame FilterInfo::GetFrame(int n, IScriptEnvironment* env) | |
| 2062 | { | ||
| 2063 | ✗ | PVideoFrame frame = vii.HasVideo() ? child->GetFrame(n, env) : env->NewVideoFrame(vi); | |
| 2064 | |||
| 2065 | ✗ | if ( !vii.HasVideo() ) { | |
| 2066 | ✗ | memset(frame->GetWritePtr(), 0, frame->GetPitch()*frame->GetHeight()); // Blank frame | |
| 2067 | } | ||
| 2068 | |||
| 2069 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 2070 | HDC hdcAntialias = nullptr; | ||
| 2071 | if (use_gdi) { | ||
| 2072 | hdcAntialias = antialiaser->GetDC(); | ||
| 2073 | if (!hdcAntialias) | ||
| 2074 | return frame; | ||
| 2075 | } else if (current_font == nullptr) | ||
| 2076 | return frame; | ||
| 2077 | #else | ||
| 2078 | ✗ | if (current_font == nullptr) | |
| 2079 | ✗ | return frame; | |
| 2080 | #endif | ||
| 2081 | ✗ | const char* c_space = "Unknown"; | |
| 2082 | ✗ | const char* s_type = t_NONE; | |
| 2083 | const char* s_parity; | ||
| 2084 | char text[1024]; | ||
| 2085 | int tlen; | ||
| 2086 | ✗ | std::string chn_layout_str; | |
| 2087 | |||
| 2088 | ✗ | if (vii.HasVideo()) { | |
| 2089 | ✗ | c_space = GetPixelTypeName(vii.pixel_type); | |
| 2090 | ✗ | if (*c_space == '\0') | |
| 2091 | ✗ | c_space = "Unknown"; | |
| 2092 | ✗ | if (vii.IsFieldBased()) { | |
| 2093 | ✗ | if (child->GetParity(n)) { | |
| 2094 | ✗ | s_parity = t_STFF; | |
| 2095 | } | ||
| 2096 | else { | ||
| 2097 | ✗ | s_parity = t_SBFF; | |
| 2098 | } | ||
| 2099 | } | ||
| 2100 | else { | ||
| 2101 | ✗ | if (child->GetParity(n)) { | |
| 2102 | ✗ | s_parity = vii.IsTFF() ? t_ATFF : t_TFF; | |
| 2103 | } | ||
| 2104 | else { | ||
| 2105 | ✗ | s_parity = vii.IsBFF() ? t_ABFF : t_BFF; | |
| 2106 | } | ||
| 2107 | } | ||
| 2108 | ✗ | uint64_t vLenInMsecs = (uint64_t)(1000.0 * (double)vii.num_frames * (double)vii.fps_denominator / (double)vii.fps_numerator); | |
| 2109 | ✗ | uint64_t cPosInMsecs = (uint64_t)(1000.0 * (double)n * (double)vii.fps_denominator / (double)vii.fps_numerator); | |
| 2110 | |||
| 2111 | ✗ | tlen = snprintf(text, sizeof(text), | |
| 2112 | "Frame: %8u of %-8u\n" // 28 | ||
| 2113 | "Time: %02" PRIu64 ":%02d:%02d.%03d of %02" PRIu64 ":%02d:%02d.%03d\n" // 35 | ||
| 2114 | "ColorSpace: %s, BitsPerComponent: %u\n" // 18=13+5 | ||
| 2115 | // "Bits per component: %2u\n" // 22 | ||
| 2116 | "Width:%4u pixels, Height:%4u pixels\n" // 39 | ||
| 2117 | "Frames per second: %7.4f (%u/%u)\n" // 51=31+20 | ||
| 2118 | "FieldBased (Separated) Video: %s\n" // 35=32+3 | ||
| 2119 | "Parity: %s\n" // 35=9+26 | ||
| 2120 | "Video Pitch: %5u bytes.\n" // 25 | ||
| 2121 | "Has Audio: %s\n" // 15=12+3 | ||
| 2122 | // "123456789012345678901234567890123456789012345678901234567890\n" // test | ||
| 2123 | ✗ | , n, vii.num_frames | |
| 2124 | ✗ | , (cPosInMsecs / (60 * 60 * 1000)), (int)((cPosInMsecs / (60 * 1000)) % 60), (int)((cPosInMsecs / 1000) % 60), (int)(cPosInMsecs % 1000), | |
| 2125 | ✗ | (vLenInMsecs / (60 * 60 * 1000)), (int)((vLenInMsecs / (60 * 1000)) % 60), (int)((vLenInMsecs / 1000) % 60), (int)(vLenInMsecs % 1000) | |
| 2126 | , c_space | ||
| 2127 | ✗ | , vii.BitsPerComponent() | |
| 2128 | ✗ | , vii.width, vii.height | |
| 2129 | ✗ | , (float)vii.fps_numerator / (float)vii.fps_denominator, vii.fps_numerator, vii.fps_denominator | |
| 2130 | ✗ | , vii.IsFieldBased() ? t_YES : t_NO | |
| 2131 | , s_parity | ||
| 2132 | , frame->GetPitch() | ||
| 2133 | ✗ | , vii.HasAudio() ? t_YES : t_NO | |
| 2134 | ); | ||
| 2135 | } | ||
| 2136 | else { | ||
| 2137 | ✗ | tlen = snprintf(text, sizeof(text), | |
| 2138 | "Frame: %8u of %-8u\n" | ||
| 2139 | "Has Video: NO\n" | ||
| 2140 | "Has Audio: %s\n" | ||
| 2141 | , n, vi.num_frames | ||
| 2142 | ✗ | , vii.HasAudio() ? t_YES : t_NO | |
| 2143 | ); | ||
| 2144 | } | ||
| 2145 | ✗ | if (vii.HasAudio()) { | |
| 2146 | ✗ | if (vii.SampleType() == SAMPLE_INT8) s_type = t_INT8; | |
| 2147 | ✗ | else if (vii.SampleType() == SAMPLE_INT16) s_type = t_INT16; | |
| 2148 | ✗ | else if (vii.SampleType() == SAMPLE_INT24) s_type = t_INT24; | |
| 2149 | ✗ | else if (vii.SampleType() == SAMPLE_INT32) s_type = t_INT32; | |
| 2150 | ✗ | else if (vii.SampleType() == SAMPLE_FLOAT) s_type = t_FLOAT32; | |
| 2151 | |||
| 2152 | ✗ | uint64_t aLenInMsecs = static_cast<uint64_t>(1000.0 * (double)vii.num_audio_samples / (double)vii.audio_samples_per_second); | |
| 2153 | ✗ | tlen += snprintf(text + tlen, sizeof(text) - tlen, | |
| 2154 | "Audio Channels: %-8u\n" // 25 | ||
| 2155 | "Sample Type: %s\n" // 28=14+14 | ||
| 2156 | "Samples Per Second: %5d\n" // 26 | ||
| 2157 | "Audio length: %" PRIu64 " samples. %02" PRIu64 ":%02d:%02d.%03d\n" // 57=37+20 | ||
| 2158 | ✗ | , vii.AudioChannels() | |
| 2159 | , s_type | ||
| 2160 | ✗ | , vii.audio_samples_per_second | |
| 2161 | ✗ | , vii.num_audio_samples, | |
| 2162 | ✗ | (aLenInMsecs / (60 * 60 * 1000)), (int)((aLenInMsecs / (60 * 1000)) % 60), (int)((aLenInMsecs / 1000) % 60), (int)(aLenInMsecs % 1000) | |
| 2163 | ); | ||
| 2164 | ✗ | if (vi.IsChannelMaskKnown()) { | |
| 2165 | ✗ | chn_layout_str = channel_layout_to_str(vi.GetChannelMask()); | |
| 2166 | ✗ | tlen += snprintf(text + tlen, sizeof(text) - tlen, | |
| 2167 | "Channel mask: %s\n", chn_layout_str.c_str()); | ||
| 2168 | } | ||
| 2169 | } | ||
| 2170 | else { | ||
| 2171 | ✗ | if (cpu) { | |
| 2172 | ✗ | strcpy(text + tlen, "\n"); | |
| 2173 | ✗ | tlen += 1; | |
| 2174 | } | ||
| 2175 | } | ||
| 2176 | ✗ | if (cpu) { | |
| 2177 | // CPU capabilities | ||
| 2178 | ✗ | tlen += snprintf(text + tlen, sizeof(text) - tlen, | |
| 2179 | "CPU: %s\n" | ||
| 2180 | #ifdef INTEL_INTRINSICS | ||
| 2181 | ✗ | , GetCpuMsg(env, false).c_str() | |
| 2182 | #else | ||
| 2183 | , GetCpuMsg(env).c_str() | ||
| 2184 | #endif | ||
| 2185 | ); | ||
| 2186 | #ifdef INTEL_INTRINSICS | ||
| 2187 | // AVX512 flags in new line (too long) | ||
| 2188 | ✗ | std::string avx512 = GetCpuMsg(env, true); | |
| 2189 | ✗ | if (avx512.length() > 0) { | |
| 2190 | ✗ | tlen += snprintf(text + tlen, sizeof(text) - tlen, | |
| 2191 | "%s\n" | ||
| 2192 | , avx512.c_str() | ||
| 2193 | ); | ||
| 2194 | } | ||
| 2195 | #endif | ||
| 2196 | // Cache size info in new line | ||
| 2197 | ✗ | std::string cache_info = GetCacheInfo(env); | |
| 2198 | ✗ | if (cache_info.length() > 0) { | |
| 2199 | ✗ | tlen += snprintf(text + tlen, sizeof(text) - tlen, | |
| 2200 | "%s\n", | ||
| 2201 | cache_info.c_str() | ||
| 2202 | ); | ||
| 2203 | } | ||
| 2204 | ✗ | } // show cpu capabilities | |
| 2205 | |||
| 2206 | // Windows GDI: aligns the whole box, its content is kept top left aligned | ||
| 2207 | // "Text" fixed font mode: individual lines are aligned as well | ||
| 2208 | |||
| 2209 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 2210 | if (use_gdi) { | ||
| 2211 | // So far RECT dimensions were hardcoded: RECT r = { 32, 16, min(3440,vi.width * 8), 900*2 }; | ||
| 2212 | // More flexible way: get text extent | ||
| 2213 | RECT r; | ||
| 2214 | |||
| 2215 | // DrawText calculates the extent as plus one line if text ends with an empty \n | ||
| 2216 | // thus calculation of vertical center and bottom aligned text would be wrong. | ||
| 2217 | // Cut ending LFs | ||
| 2218 | while (tlen > 1 && text[tlen - 1] == '\n') { | ||
| 2219 | text[tlen - 1] = 0; | ||
| 2220 | tlen--; | ||
| 2221 | } | ||
| 2222 | |||
| 2223 | RECT r0 = { 0, 0, 100, 100 }; | ||
| 2224 | |||
| 2225 | // always draw box at top left aligned, we calculate coordinates instead | ||
| 2226 | // depending on the given alignment | ||
| 2227 | SetTextAlign(hdcAntialias, TA_TOP | TA_LEFT); | ||
| 2228 | // calculate box extent without drawing | ||
| 2229 | DrawText(hdcAntialias, text, -1, &r0, DT_CALCRECT | DT_NOCLIP); | ||
| 2230 | |||
| 2231 | // correct top left and right bottom of the whole info box | ||
| 2232 | int real_x = x; | ||
| 2233 | int real_y = y; | ||
| 2234 | // align vertical center | ||
| 2235 | if (align == 4 || align == 5 || align == 6) { | ||
| 2236 | real_y -= (int)((r0.bottom- r0.top) / 2.0 + 0.5); | ||
| 2237 | } | ||
| 2238 | // align vertical bottom | ||
| 2239 | if (align == 1 || align == 2 || align == 3) { | ||
| 2240 | real_y -= (r0.bottom - r0.top); | ||
| 2241 | } | ||
| 2242 | // align horizontal center | ||
| 2243 | if (align == 8 || align == 5 || align == 2) { | ||
| 2244 | real_x -= (int)((r0.right - r0.left) / 2.0 + 0.5); | ||
| 2245 | } | ||
| 2246 | // align horizontal right | ||
| 2247 | if (align == 9 || align == 6 || align == 3) { | ||
| 2248 | real_x -= (r0.right - r0.left); | ||
| 2249 | } | ||
| 2250 | |||
| 2251 | int right = real_x + (int)r0.right; | ||
| 2252 | int bottom = real_y + (int)r0.bottom; | ||
| 2253 | // not left aligned: crop to visible right | ||
| 2254 | if (!(align == 9 || align == 6 || align == 3)) | ||
| 2255 | right = min(right, vi.width * 8 - 1); | ||
| 2256 | // not bottom aligned: crop to visible bottom | ||
| 2257 | if (!(align == 1 || align == 2 || align == 3)) | ||
| 2258 | bottom = min(bottom, vi.height * 8 - 1); | ||
| 2259 | |||
| 2260 | r = { real_x, real_y, right, bottom }; | ||
| 2261 | |||
| 2262 | // NOCLIP: draw lines if they are partially visible | ||
| 2263 | DrawText(hdcAntialias, text, -1, &r, DT_NOCLIP); | ||
| 2264 | GdiFlush(); | ||
| 2265 | |||
| 2266 | env->MakeWritable(&frame); | ||
| 2267 | frame->GetWritePtr(); // Bump sequence_number | ||
| 2268 | int dst_pitch = frame->GetPitch(); | ||
| 2269 | antialiaser->Apply(vi, &frame, dst_pitch); | ||
| 2270 | } else { | ||
| 2271 | env->MakeWritable(&frame); | ||
| 2272 | frame->GetWritePtr(); // Bump sequence_number | ||
| 2273 | |||
| 2274 | bool utf8 = false; | ||
| 2275 | std::string s_utf8 = charToUtf8(text, utf8); | ||
| 2276 | int lsp = 0; | ||
| 2277 | SimpleTextOutW_multi(current_font.get(), vi, frame, x, y, s_utf8, false, text_color, halo_color, true, align, lsp, chromaplacement); | ||
| 2278 | } | ||
| 2279 | #else | ||
| 2280 | ✗ | env->MakeWritable(&frame); | |
| 2281 | ✗ | frame->GetWritePtr(); // Bump sequence_number | |
| 2282 | |||
| 2283 | // AVS_POSIX: utf8 is always true, here n/a | ||
| 2284 | ✗ | bool utf8 = false; | |
| 2285 | ✗ | std::string s_utf8 = charToUtf8(text, utf8); | |
| 2286 | |||
| 2287 | ✗ | int lsp = 0; // line spacing n/a | |
| 2288 | ✗ | SimpleTextOutW_multi(current_font.get(), vi, frame, x, y, s_utf8, false, text_color, halo_color, true, align, lsp, chromaplacement); | |
| 2289 | #endif | ||
| 2290 | ✗ | return frame; | |
| 2291 | ✗ | } | |
| 2292 | |||
| 2293 | ✗ | AVSValue __cdecl FilterInfo::Create(AVSValue args, void*, IScriptEnvironment* env) | |
| 2294 | { | ||
| 2295 | // 0 1 2 3 4 5 6 7 8 9 10 11 12 | ||
| 2296 | // c[font]s[size]f[text_color]i[halo_color]i[bold]b[italic]b[noaa]b[cpu]b[x]f[y]f[align]i[gdi]b | ||
| 2297 | ✗ | PClip clip = args[0].AsClip(); | |
| 2298 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 2299 | const bool gdi = args[12].AsBool(true); | ||
| 2300 | #else | ||
| 2301 | ✗ | const bool gdi = false; | |
| 2302 | #endif | ||
| 2303 | ✗ | const char* font = args[1].AsString(gdi ? "Courier New" : "Terminus"); | |
| 2304 | ✗ | const int upper_limit = gdi ? -1 : 32; | |
| 2305 | ✗ | int size = gdi ? int(args[2].AsFloat(0) * 8 + 0.5) : int(args[2].AsFloat(0)); | |
| 2306 | ✗ | if (!args[2].Defined() || size < 0) | |
| 2307 | ✗ | size = CalcFontSizeForInfo(clip->GetVideoInfo().width, clip->GetVideoInfo().height, size < 0, upper_limit, gdi); | |
| 2308 | ✗ | const int text_color = args[3].AsInt(0xFFFF00); | |
| 2309 | ✗ | const int halo_color = args[4].AsInt(0); | |
| 2310 | ✗ | const bool bold = args[5].AsBool(gdi); | |
| 2311 | ✗ | const bool italic = args[6].AsBool(false); | |
| 2312 | ✗ | const bool noaa = args[7].AsBool(false); | |
| 2313 | ✗ | const bool cpu = args[8].AsBool(true); | |
| 2314 | |||
| 2315 | ✗ | const int align = args[11].AsInt(7); // default top left | |
| 2316 | |||
| 2317 | ✗ | const int info_default_x = 4; // subtitle: 8 | |
| 2318 | ✗ | const int PIXEL_MUL_FACTOR = gdi ? 8 : 1; | |
| 2319 | // similar to SubTitle | ||
| 2320 | int defx, defy; | ||
| 2321 | ✗ | bool x_center = false; | |
| 2322 | ✗ | bool y_center = false; | |
| 2323 | |||
| 2324 | ✗ | switch (align) { | |
| 2325 | ✗ | case 1: case 4: case 7: defx = 8; break; | |
| 2326 | ✗ | case 2: case 5: case 8: | |
| 2327 | ✗ | defx = 0; // n/a if not set later | |
| 2328 | ✗ | x_center = true; | |
| 2329 | ✗ | break; | |
| 2330 | ✗ | case 3: case 6: case 9: defx = clip->GetVideoInfo().width - info_default_x; break; // subtitle: 8 | |
| 2331 | ✗ | default: defx = info_default_x; break; | |
| 2332 | } | ||
| 2333 | |||
| 2334 | ✗ | switch (align) { | |
| 2335 | ✗ | case 1: case 2: case 3: defy = clip->GetVideoInfo().height - 2; break; // bottom alignment 2 pixel above | |
| 2336 | ✗ | case 4: case 5: case 6: | |
| 2337 | ✗ | defy = 0; // n/a if not set later | |
| 2338 | ✗ | y_center = true; | |
| 2339 | ✗ | break; | |
| 2340 | ✗ | case 7: case 8: case 9: defy = 0; break; | |
| 2341 | ✗ | default: defy = (size + 4) / 8; break; | |
| 2342 | } | ||
| 2343 | |||
| 2344 | ✗ | const bool isXdefined = args[9].Defined(); | |
| 2345 | ✗ | const bool isYdefined = args[10].Defined(); | |
| 2346 | |||
| 2347 | ✗ | int x = int(args[9].AsDblDef(defx) * PIXEL_MUL_FACTOR + 0.5); | |
| 2348 | ✗ | int y = int(args[10].AsDblDef(defy) * PIXEL_MUL_FACTOR + 0.5); | |
| 2349 | |||
| 2350 | ✗ | if (!isXdefined && x_center) | |
| 2351 | ✗ | x = (clip->GetVideoInfo().width >> 1) * PIXEL_MUL_FACTOR; | |
| 2352 | |||
| 2353 | ✗ | if (!isYdefined && y_center) | |
| 2354 | ✗ | y = (clip->GetVideoInfo().height >> 1) * PIXEL_MUL_FACTOR; | |
| 2355 | |||
| 2356 | |||
| 2357 | ✗ | if ((align < 1) || (align > 9)) | |
| 2358 | ✗ | env->ThrowError("Info: Align values are 1 - 9 mapped to your numeric pad"); | |
| 2359 | |||
| 2360 | ✗ | return new FilterInfo(clip, font, size, text_color, halo_color, bold, italic, noaa, cpu, x, y, align, gdi, env); | |
| 2361 | ✗ | } | |
| 2362 | |||
| 2363 | |||
| 2364 | |||
| 2365 | /************************************ | ||
| 2366 | ******* Compare Filter ******* | ||
| 2367 | ***********************************/ | ||
| 2368 | |||
| 2369 | 6 | Compare::Compare(PClip _child1, PClip _child2, const char* channels, const char *fname, bool _show_graph, bool _gdi, IScriptEnvironment* env) | |
| 2370 | : GenericVideoFilter(_child1), | ||
| 2371 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 2372 | use_gdi(_gdi), | ||
| 2373 | antialiaser(_gdi ? std::make_unique<Antialiaser>(vi.width, vi.height, "Courier New", 16 * 8, | ||
| 2374 | (vi.IsYUV() || vi.IsYUVA()) ? 0xD21092 : 0xFFFF00, | ||
| 2375 | (vi.IsYUV() || vi.IsYUVA()) ? 0x108080 : 0, | ||
| 2376 | true, false, false, | ||
| 2377 | env->GetCPUFlagsEx(), ChromaLocation_e::AVS_CHROMA_CENTER, /* quick */ | ||
| 2378 | 0, 0, false) : nullptr), | ||
| 2379 | #else | ||
| 2380 | 6 | use_gdi(false), | |
| 2381 | #endif | ||
| 2382 |
1/2✓ Branch 6 → 7 taken 6 times.
✗ Branch 6 → 177 not taken.
|
6 | child2(_child2), |
| 2383 | 6 | log(nullptr), | |
| 2384 | 6 | show_graph(_show_graph), | |
| 2385 | 6 | framecount(0), | |
| 2386 |
5/8✓ Branch 7 → 8 taken 6 times.
✗ Branch 7 → 175 not taken.
✓ Branch 8 → 9 taken 5 times.
✓ Branch 8 → 11 taken 1 time.
✓ Branch 9 → 10 taken 5 times.
✗ Branch 9 → 175 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 5 times.
|
6 | text_color((vi.IsYUV() || vi.IsYUVA()) ? 0xD21092 : 0xFFFF00), |
| 2387 |
7/12✓ Branch 2 → 3 taken 6 times.
✗ Branch 2 → 171 not taken.
✓ Branch 3 → 4 taken 6 times.
✗ Branch 3 → 169 not taken.
✓ Branch 13 → 14 taken 6 times.
✗ Branch 13 → 175 not taken.
✓ Branch 14 → 15 taken 5 times.
✓ Branch 14 → 17 taken 1 time.
✓ Branch 15 → 16 taken 5 times.
✗ Branch 15 → 175 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 5 times.
|
12 | halo_color((vi.IsYUV() || vi.IsYUVA()) ? 0x108080 : 0) |
| 2388 | { | ||
| 2389 |
1/2✓ Branch 20 → 21 taken 6 times.
✗ Branch 20 → 175 not taken.
|
6 | const VideoInfo& vi2 = child2->GetVideoInfo(); |
| 2390 | 6 | psnrs = 0; | |
| 2391 | |||
| 2392 |
2/4✓ Branch 21 → 22 taken 6 times.
✗ Branch 21 → 175 not taken.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 6 times.
|
6 | if (!vi.IsSameColorspace(vi2)) |
| 2393 | ✗ | env->ThrowError("Compare: Clips are not same colorspace."); | |
| 2394 | |||
| 2395 |
2/4✓ Branch 24 → 25 taken 6 times.
✗ Branch 24 → 26 not taken.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 6 times.
|
6 | if (vi.width != vi2.width || vi.height != vi2.height) |
| 2396 | ✗ | env->ThrowError("Compare: Clips must have same size."); | |
| 2397 | |||
| 2398 |
13/26✓ Branch 27 → 28 taken 6 times.
✗ Branch 27 → 175 not taken.
✓ Branch 28 → 29 taken 6 times.
✗ Branch 28 → 40 not taken.
✓ Branch 29 → 30 taken 6 times.
✗ Branch 29 → 175 not taken.
✓ Branch 30 → 31 taken 6 times.
✗ Branch 30 → 40 not taken.
✓ Branch 31 → 32 taken 6 times.
✗ Branch 31 → 175 not taken.
✓ Branch 32 → 33 taken 4 times.
✓ Branch 32 → 40 taken 2 times.
✓ Branch 33 → 34 taken 4 times.
✗ Branch 33 → 175 not taken.
✓ Branch 34 → 35 taken 3 times.
✓ Branch 34 → 40 taken 1 time.
✓ Branch 35 → 36 taken 3 times.
✗ Branch 35 → 175 not taken.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 40 taken 3 times.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 175 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 6 times.
|
6 | if (!(vi.IsRGB24() || vi.IsYUY2() || vi.IsRGB32() || vi.IsPlanar() || vi.IsRGB48() || vi.IsRGB64())) |
| 2399 | ✗ | env->ThrowError("Compare: Clips have unknown pixel format. RGB24/32/48/64, YUY2 and YUV/RGB Planar supported."); | |
| 2400 | |||
| 2401 |
1/2✓ Branch 43 → 44 taken 6 times.
✗ Branch 43 → 175 not taken.
|
6 | pixelsize = vi.ComponentSize(); |
| 2402 |
1/2✓ Branch 44 → 45 taken 6 times.
✗ Branch 44 → 175 not taken.
|
6 | bits_per_pixel = vi.BitsPerComponent(); |
| 2403 | |||
| 2404 |
1/2✗ Branch 45 → 46 not taken.
✓ Branch 45 → 47 taken 6 times.
|
6 | if (pixelsize == 4) |
| 2405 | ✗ | env->ThrowError("Compare: Float pixel format not supported."); | |
| 2406 | |||
| 2407 |
1/2✗ Branch 47 → 48 not taken.
✓ Branch 47 → 63 taken 6 times.
|
6 | if (channels[0] == 0) { |
| 2408 | ✗ | if (vi.IsRGB()) | |
| 2409 | ✗ | channels = "RGB"; | |
| 2410 | ✗ | else if (vi.IsY()) | |
| 2411 | ✗ | channels = "Y"; | |
| 2412 | ✗ | else if (vi.IsYUV() || vi.IsYUVA()) | |
| 2413 | ✗ | channels = "YUV"; | |
| 2414 | ✗ | else env->ThrowError("Compare: Clips have unknown colorspace. RGB and YUV supported."); | |
| 2415 | } | ||
| 2416 | |||
| 2417 | 6 | planar_plane = 0; | |
| 2418 | 6 | mask = 0; | |
| 2419 | 6 | const size_t length = strlen(channels); | |
| 2420 |
2/2✓ Branch 132 → 64 taken 14 times.
✓ Branch 132 → 133 taken 6 times.
|
20 | for (size_t i = 0; i < length; i++) { |
| 2421 |
7/10✓ Branch 64 → 65 taken 14 times.
✗ Branch 64 → 175 not taken.
✓ Branch 65 → 66 taken 13 times.
✓ Branch 65 → 69 taken 1 time.
✓ Branch 66 → 67 taken 13 times.
✗ Branch 66 → 175 not taken.
✓ Branch 67 → 68 taken 13 times.
✗ Branch 67 → 69 not taken.
✓ Branch 70 → 71 taken 13 times.
✓ Branch 70 → 92 taken 1 time.
|
14 | if (vi.IsRGB() && !vi.IsPlanar()) { |
| 2422 |
3/5✓ Branch 71 → 72 taken 4 times.
✓ Branch 71 → 73 taken 4 times.
✓ Branch 71 → 74 taken 5 times.
✗ Branch 71 → 75 not taken.
✗ Branch 71 → 83 not taken.
|
13 | switch (channels[i]) { |
| 2423 | 4 | case 'b': | |
| 2424 | 4 | case 'B': mask |= 0x000000ff; mask64 |= 0x000000000000ffffull; break; | |
| 2425 | 4 | case 'g': | |
| 2426 | 4 | case 'G': mask |= 0x0000ff00; mask64 |= 0x00000000ffff0000ull; break; | |
| 2427 | 5 | case 'r': | |
| 2428 | 5 | case 'R': mask |= 0x00ff0000; mask64 |= 0x0000ffff00000000ull; break; | |
| 2429 | ✗ | case 'a': | |
| 2430 | ✗ | case 'A': mask |= 0xff000000; mask64 |= 0xffff000000000000ull; if (vi.IsRGB32() || vi.IsRGB64()) break; // else no alpha -> fall thru | |
| 2431 | ✗ | default: env->ThrowError("Compare: invalid channel: %c", channels[i]); | |
| 2432 | } | ||
| 2433 |
7/10✓ Branch 84 → 85 taken 13 times.
✗ Branch 84 → 175 not taken.
✓ Branch 85 → 86 taken 13 times.
✗ Branch 85 → 88 not taken.
✓ Branch 86 → 87 taken 13 times.
✗ Branch 86 → 175 not taken.
✓ Branch 87 → 88 taken 7 times.
✓ Branch 87 → 89 taken 6 times.
✓ Branch 90 → 91 taken 7 times.
✓ Branch 90 → 131 taken 6 times.
|
13 | if (vi.IsRGB24() || vi.IsRGB48()) mask &= 0x00ffffff; // no alpha channel in RGB24 |
| 2434 |
2/4✓ Branch 92 → 93 taken 1 time.
✗ Branch 92 → 175 not taken.
✓ Branch 93 → 94 taken 1 time.
✗ Branch 93 → 126 not taken.
|
1 | } else if (vi.IsPlanar()) { |
| 2435 |
3/10✓ Branch 94 → 95 taken 1 time.
✗ Branch 94 → 175 not taken.
✗ Branch 95 → 96 not taken.
✓ Branch 95 → 98 taken 1 time.
✗ Branch 96 → 97 not taken.
✗ Branch 96 → 175 not taken.
✗ Branch 97 → 98 not taken.
✗ Branch 97 → 99 not taken.
✓ Branch 100 → 101 taken 1 time.
✗ Branch 100 → 117 not taken.
|
1 | if(vi.IsYUV() || vi.IsYUVA()) { |
| 2436 |
1/5✓ Branch 101 → 102 taken 1 time.
✗ Branch 101 → 103 not taken.
✗ Branch 101 → 104 not taken.
✗ Branch 101 → 105 not taken.
✗ Branch 101 → 108 not taken.
|
1 | switch (channels[i]) { |
| 2437 | 1 | case 'y': | |
| 2438 | 1 | case 'Y': mask |= 0xffffffff; planar_plane |= PLANAR_Y; break; | |
| 2439 | ✗ | case 'u': | |
| 2440 | ✗ | case 'U': mask |= 0xffffffff; planar_plane |= PLANAR_U; break; | |
| 2441 | ✗ | case 'v': | |
| 2442 | ✗ | case 'V': mask |= 0xffffffff; planar_plane |= PLANAR_V; break; | |
| 2443 | ✗ | case 'a': | |
| 2444 | ✗ | case 'A': mask |= 0xffffffff; planar_plane |= PLANAR_A; if (vi.IsYUVA()) break; // else no alpha -> fall thru | |
| 2445 | ✗ | default: env->ThrowError("Compare: invalid channel: %c", channels[i]); | |
| 2446 | } | ||
| 2447 |
5/10✓ Branch 109 → 110 taken 1 time.
✗ Branch 109 → 175 not taken.
✓ Branch 110 → 111 taken 1 time.
✗ Branch 110 → 114 not taken.
✓ Branch 111 → 112 taken 1 time.
✗ Branch 111 → 113 not taken.
✗ Branch 112 → 113 not taken.
✓ Branch 112 → 114 taken 1 time.
✗ Branch 115 → 116 not taken.
✓ Branch 115 → 125 taken 1 time.
|
1 | if (vi.IsY() && ((planar_plane & PLANAR_U) || (planar_plane & PLANAR_V))) { |
| 2448 | ✗ | env->ThrowError("Compare: invalid channel: %c for greyscale clip", channels[i]); | |
| 2449 | } | ||
| 2450 | } else { | ||
| 2451 | // planar RGB, planar RGBA | ||
| 2452 | ✗ | switch (channels[i]) { | |
| 2453 | ✗ | case 'r': | |
| 2454 | ✗ | case 'R': mask |= 0xffffffff; planar_plane |= PLANAR_R; break; | |
| 2455 | ✗ | case 'g': | |
| 2456 | ✗ | case 'G': mask |= 0xffffffff; planar_plane |= PLANAR_G; break; | |
| 2457 | ✗ | case 'b': | |
| 2458 | ✗ | case 'B': mask |= 0xffffffff; planar_plane |= PLANAR_B; break; | |
| 2459 | ✗ | case 'a': | |
| 2460 | ✗ | case 'A': mask |= 0xffffffff; planar_plane |= PLANAR_A; if (vi.IsPlanarRGBA()) break; // else no alpha -> fall thru | |
| 2461 | ✗ | default: env->ThrowError("Compare: invalid channel: %c", channels[i]); | |
| 2462 | } | ||
| 2463 | } | ||
| 2464 | } else { // YUY2 | ||
| 2465 | ✗ | switch (channels[i]) { | |
| 2466 | ✗ | case 'y': | |
| 2467 | ✗ | case 'Y': mask |= 0x00ff00ff; break; | |
| 2468 | ✗ | case 'u': | |
| 2469 | ✗ | case 'U': mask |= 0x0000ff00; break; | |
| 2470 | ✗ | case 'v': | |
| 2471 | ✗ | case 'V': mask |= 0xff000000; break; | |
| 2472 | ✗ | default: env->ThrowError("Compare: invalid channel: %c", channels[i]); | |
| 2473 | } | ||
| 2474 | } | ||
| 2475 | } | ||
| 2476 | |||
| 2477 | 6 | masked_bytes = 0; | |
| 2478 |
2/2✓ Branch 135 → 134 taken 19 times.
✓ Branch 135 → 136 taken 6 times.
|
25 | for (uint32_t temp = mask; temp != 0; temp >>=8) |
| 2479 | 19 | masked_bytes += (temp & 1); | |
| 2480 | |||
| 2481 |
2/2✓ Branch 136 → 137 taken 5 times.
✓ Branch 136 → 145 taken 1 time.
|
6 | if (fname[0] != 0) { |
| 2482 |
1/2✓ Branch 137 → 138 taken 5 times.
✗ Branch 137 → 175 not taken.
|
5 | log = fopen(fname, "wt"); |
| 2483 |
1/2✓ Branch 138 → 139 taken 5 times.
✗ Branch 138 → 144 not taken.
|
5 | if (log) { |
| 2484 |
1/2✓ Branch 139 → 140 taken 5 times.
✗ Branch 139 → 175 not taken.
|
5 | fprintf(log,"Comparing channel(s) %s\n\n",channels); |
| 2485 |
1/2✓ Branch 140 → 141 taken 5 times.
✗ Branch 140 → 175 not taken.
|
5 | fprintf(log," Mean Max Max \n"); |
| 2486 |
1/2✓ Branch 141 → 142 taken 5 times.
✗ Branch 141 → 175 not taken.
|
5 | fprintf(log," Absolute Mean Pos. Neg. \n"); |
| 2487 |
1/2✓ Branch 142 → 143 taken 5 times.
✗ Branch 142 → 175 not taken.
|
5 | fprintf(log," Frame Dev. Dev. Dev. Dev. PSNR (dB) \n"); |
| 2488 |
1/2✓ Branch 143 → 153 taken 5 times.
✗ Branch 143 → 175 not taken.
|
5 | fprintf(log,"-----------------------------------------------------\n"); |
| 2489 | } else | ||
| 2490 | ✗ | env->ThrowError("Compare: unable to create file %s", fname); | |
| 2491 | } else { | ||
| 2492 |
1/2✓ Branch 145 → 146 taken 1 time.
✗ Branch 145 → 147 not taken.
|
1 | psnrs = new(std::nothrow) int[vi.num_frames]; |
| 2493 |
1/2✓ Branch 149 → 150 taken 1 time.
✗ Branch 149 → 153 not taken.
|
1 | if (psnrs) |
| 2494 |
2/2✓ Branch 152 → 151 taken 2 times.
✓ Branch 152 → 153 taken 1 time.
|
3 | for (int i = 0; i < vi.num_frames; i++) |
| 2495 | 2 | psnrs[i] = 0; | |
| 2496 | } | ||
| 2497 |
1/2✓ Branch 153 → 154 taken 6 times.
✗ Branch 153 → 168 not taken.
|
6 | if (!use_gdi) { |
| 2498 | 6 | const int size = 16; | |
| 2499 |
1/2✓ Branch 154 → 155 taken 6 times.
✗ Branch 154 → 172 not taken.
|
6 | current_font = GetBitmapFont(size, "Terminus", false, false); |
| 2500 | 6 | chromaplacement = ChromaLocation_e::AVS_CHROMA_LEFT; | |
| 2501 |
1/2✗ Branch 158 → 159 not taken.
✓ Branch 158 → 168 taken 6 times.
|
6 | if (current_font == nullptr) { |
| 2502 | ✗ | current_font = GetBitmapFont(size, "", false, false); | |
| 2503 | ✗ | if (current_font == nullptr) | |
| 2504 | ✗ | current_font = GetBitmapFont(size, "", true, false); | |
| 2505 | } | ||
| 2506 | } | ||
| 2507 | 6 | } | |
| 2508 | |||
| 2509 | |||
| 2510 | 6 | Compare::~Compare() | |
| 2511 | { | ||
| 2512 |
2/2✓ Branch 2 → 3 taken 5 times.
✓ Branch 2 → 10 taken 1 time.
|
6 | if (log) { |
| 2513 | 5 | fprintf(log,"\n\n\nTotal frames processed: %d\n\n", framecount); | |
| 2514 | 5 | fprintf(log," Minimum Average Maximum\n"); | |
| 2515 | 5 | fprintf(log,"Mean Absolute Deviation: %9.4f %9.4f %9.4f\n", MAD_min, MAD_tot/framecount, MAD_max); | |
| 2516 | 5 | fprintf(log," Mean Deviation: %+9.4f %+9.4f %+9.4f\n", MD_min, MD_tot/framecount, MD_max); | |
| 2517 | 5 | fprintf(log," PSNR: %9.4f %9.4f %9.4f\n", PSNR_min, PSNR_tot/framecount, PSNR_max); | |
| 2518 | 5 | double factor = (1 << bits_per_pixel) - 1; | |
| 2519 | 5 | double PSNR_overall = 10.0 * log10(bytecount_overall * factor * factor / SSD_overall); | |
| 2520 | 5 | fprintf(log," Overall PSNR: %9.4f\n", PSNR_overall); | |
| 2521 | 5 | fclose(log); | |
| 2522 | } | ||
| 2523 |
2/2✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 5 times.
|
6 | delete[] psnrs; |
| 2524 | 6 | } | |
| 2525 | |||
| 2526 | |||
| 2527 | ✗ | AVSValue __cdecl Compare::Create(AVSValue args, void*, IScriptEnvironment *env) | |
| 2528 | { | ||
| 2529 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 2530 | const bool gdi = args[5].AsBool(true); | ||
| 2531 | #else | ||
| 2532 | ✗ | const bool gdi = false; | |
| 2533 | #endif | ||
| 2534 | ✗ | return new Compare( args[0].AsClip(), // clip | |
| 2535 | ✗ | args[1].AsClip(), // base clip | |
| 2536 | ✗ | args[2].AsString(""), // channels | |
| 2537 | ✗ | args[3].AsString(""), // logfile | |
| 2538 | ✗ | args[4].AsBool(true), // show_graph | |
| 2539 | gdi, | ||
| 2540 | ✗ | env); | |
| 2541 | } | ||
| 2542 | |||
| 2543 | ✗ | static void compare_planar_c( | |
| 2544 | const BYTE * f1ptr, int pitch1, | ||
| 2545 | const BYTE * f2ptr, int pitch2, | ||
| 2546 | int rowsize, int height, | ||
| 2547 | int &SAD_sum, int &SD_sum, int &pos_D, int &neg_D, double &SSD_sum) | ||
| 2548 | { | ||
| 2549 | int row_SSD; | ||
| 2550 | |||
| 2551 | ✗ | for (int y = 0; y < height; y++) { | |
| 2552 | ✗ | row_SSD = 0; | |
| 2553 | ✗ | for (int x = 0; x < rowsize; x += 1) { | |
| 2554 | ✗ | int p1 = *(f1ptr + x); | |
| 2555 | ✗ | int p2 = *(f2ptr + x); | |
| 2556 | ✗ | int d0 = p1 - p2; | |
| 2557 | ✗ | SD_sum += d0; | |
| 2558 | ✗ | SAD_sum += abs(d0); | |
| 2559 | ✗ | row_SSD += d0 * d0; | |
| 2560 | ✗ | pos_D = max(pos_D, d0); | |
| 2561 | ✗ | neg_D = min(neg_D, d0); | |
| 2562 | } | ||
| 2563 | ✗ | SSD_sum += row_SSD; | |
| 2564 | ✗ | f1ptr += pitch1; | |
| 2565 | ✗ | f2ptr += pitch2; | |
| 2566 | } | ||
| 2567 | ✗ | } | |
| 2568 | |||
| 2569 | ✗ | static void compare_planar_uint16_t_c( | |
| 2570 | const BYTE * f1ptr8, int pitch1, | ||
| 2571 | const BYTE * f2ptr8, int pitch2, | ||
| 2572 | int rowsize, int height, | ||
| 2573 | int64_t&SAD_sum, int64_t &SD_sum, int &pos_D, int &neg_D, double &SSD_sum) | ||
| 2574 | { | ||
| 2575 | int64_t row_SSD; | ||
| 2576 | |||
| 2577 | ✗ | const uint16_t *f1ptr = reinterpret_cast<const uint16_t *>(f1ptr8); | |
| 2578 | ✗ | const uint16_t *f2ptr = reinterpret_cast<const uint16_t *>(f2ptr8); | |
| 2579 | ✗ | pitch1 /= sizeof(uint16_t); | |
| 2580 | ✗ | pitch2 /= sizeof(uint16_t); | |
| 2581 | ✗ | rowsize /= sizeof(uint16_t); | |
| 2582 | |||
| 2583 | |||
| 2584 | ✗ | for (int y = 0; y < height; y++) { | |
| 2585 | ✗ | row_SSD = 0; | |
| 2586 | ✗ | for (int x = 0; x < rowsize; x += 1) { | |
| 2587 | ✗ | int p1 = *(f1ptr + x); | |
| 2588 | ✗ | int p2 = *(f2ptr + x); | |
| 2589 | ✗ | int d0 = p1 - p2; | |
| 2590 | ✗ | SD_sum += d0; | |
| 2591 | ✗ | SAD_sum += abs(d0); | |
| 2592 | ✗ | row_SSD += d0 * d0; | |
| 2593 | ✗ | pos_D = max(pos_D, d0); | |
| 2594 | ✗ | neg_D = min(neg_D, d0); | |
| 2595 | } | ||
| 2596 | ✗ | SSD_sum += row_SSD; | |
| 2597 | ✗ | f1ptr += pitch1; | |
| 2598 | ✗ | f2ptr += pitch2; | |
| 2599 | } | ||
| 2600 | ✗ | } | |
| 2601 | |||
| 2602 | |||
| 2603 | ✗ | static void compare_c(uint32_t mask, int increment, | |
| 2604 | const BYTE * f1ptr, int pitch1, | ||
| 2605 | const BYTE * f2ptr, int pitch2, | ||
| 2606 | int rowsize, int height, | ||
| 2607 | int &SAD_sum, int &SD_sum, int &pos_D, int &neg_D, double &SSD_sum) | ||
| 2608 | { | ||
| 2609 | int row_SSD; | ||
| 2610 | |||
| 2611 | ✗ | for (int y = 0; y < height; y++) { | |
| 2612 | ✗ | row_SSD = 0; | |
| 2613 | ✗ | for (int x = 0; x < rowsize; x += increment) { | |
| 2614 | ✗ | uint32_t p1 = *(uint32_t*)(f1ptr + x) & mask; | |
| 2615 | ✗ | uint32_t p2 = *(uint32_t*)(f2ptr + x) & mask; | |
| 2616 | ✗ | int d0 = (p1 & 0xff) - (p2 & 0xff); | |
| 2617 | ✗ | int d1 = ((p1 >> 8) & 0xff) - ((p2 & 0xff00) >> 8); // ?PF why not (p2 >> 8) & 0xff as for p1? | |
| 2618 | ✗ | int d2 = ((p1 >> 16) & 0xff) - ((p2 & 0xff0000) >> 16); | |
| 2619 | ✗ | int d3 = (p1 >> 24) - (p2 >> 24); | |
| 2620 | ✗ | SD_sum += d0 + d1 + d2 + d3; | |
| 2621 | ✗ | SAD_sum += abs(d0) + abs(d1) + abs(d2) + abs(d3); | |
| 2622 | ✗ | row_SSD += d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3; | |
| 2623 | ✗ | pos_D = max(max(max(max(pos_D, d0), d1), d2), d3); | |
| 2624 | ✗ | neg_D = min(min(min(min(neg_D, d0), d1), d2), d3); | |
| 2625 | } | ||
| 2626 | ✗ | SSD_sum += row_SSD; | |
| 2627 | ✗ | f1ptr += pitch1; | |
| 2628 | ✗ | f2ptr += pitch2; | |
| 2629 | } | ||
| 2630 | ✗ | } | |
| 2631 | |||
| 2632 | 3 | static void compare_uint16_t_c(uint64_t mask64, int increment, | |
| 2633 | const BYTE * f1ptr8, int pitch1, | ||
| 2634 | const BYTE * f2ptr8, int pitch2, | ||
| 2635 | int rowsize, int height, | ||
| 2636 | int64_t& SAD_sum, int64_t &SD_sum, int &pos_D, int &neg_D, double &SSD_sum) | ||
| 2637 | { | ||
| 2638 | int64_t row_SSD; | ||
| 2639 | |||
| 2640 | 3 | const uint16_t *f1ptr = reinterpret_cast<const uint16_t *>(f1ptr8); | |
| 2641 | 3 | const uint16_t *f2ptr = reinterpret_cast<const uint16_t *>(f2ptr8); | |
| 2642 | 3 | pitch1 /= sizeof(uint16_t); | |
| 2643 | 3 | pitch2 /= sizeof(uint16_t); | |
| 2644 | 3 | rowsize /= sizeof(uint16_t); | |
| 2645 | |||
| 2646 |
2/2✓ Branch 15 → 3 taken 3 times.
✓ Branch 15 → 16 taken 3 times.
|
6 | for (int y = 0; y < height; y++) { |
| 2647 | 3 | row_SSD = 0; | |
| 2648 |
2/2✓ Branch 13 → 4 taken 12 times.
✓ Branch 13 → 14 taken 3 times.
|
15 | for (int x = 0; x < rowsize; x += increment) { |
| 2649 | 12 | uint64_t p1 = *(uint64_t *)(f1ptr + x) & mask64; | |
| 2650 | 12 | uint64_t p2 = *(uint64_t *)(f2ptr + x) & mask64; | |
| 2651 | 12 | int d0 = (p1 & 0xffff) - (p2 & 0xffff); | |
| 2652 | 12 | int d1 = ((p1 >> 16) & 0xffff) - ((p2 & 0xffff0000) >> 16); // ?PF why not (p2 >> 16) & 0xffff as for p1? | |
| 2653 | 12 | int d2 = ((p1 >> 32) & 0xffff) - ((p2 & 0xffff00000000ull) >> 32); | |
| 2654 | 12 | int d3 = (p1 >> 48) - (p2 >> 48); | |
| 2655 | 12 | SD_sum += d0 + d1 + d2 + d3; | |
| 2656 | 12 | SAD_sum += abs(d0) + abs(d1) + abs(d2) + abs(d3); | |
| 2657 | 12 | row_SSD += d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3; | |
| 2658 | 12 | pos_D = max(max(max(max(pos_D, d0), d1), d2), d3); | |
| 2659 | 12 | neg_D = min(min(min(min(neg_D, d0), d1), d2), d3); | |
| 2660 | } | ||
| 2661 | 3 | SSD_sum += row_SSD; | |
| 2662 | 3 | f1ptr += pitch1; | |
| 2663 | 3 | f2ptr += pitch2; | |
| 2664 | } | ||
| 2665 | 3 | } | |
| 2666 | |||
| 2667 | |||
| 2668 | |||
| 2669 | 4 | PVideoFrame __stdcall Compare::GetFrame(int n, IScriptEnvironment* env) | |
| 2670 | { | ||
| 2671 |
1/2✓ Branch 3 → 4 taken 4 times.
✗ Branch 3 → 217 not taken.
|
4 | PVideoFrame f1 = child->GetFrame(n, env); |
| 2672 |
1/2✓ Branch 5 → 6 taken 4 times.
✗ Branch 5 → 215 not taken.
|
4 | PVideoFrame f2 = child2->GetFrame(n, env); |
| 2673 | |||
| 2674 | 4 | int SD = 0; | |
| 2675 | 4 | int64_t SD_64 = 0; | |
| 2676 | 4 | int SAD = 0; | |
| 2677 | 4 | int64_t SAD_64 = 0; | |
| 2678 | 4 | int pos_D = 0; | |
| 2679 | 4 | int neg_D = 0; | |
| 2680 | 4 | double SSD = 0; | |
| 2681 | |||
| 2682 | 4 | int bytecount = 0; | |
| 2683 | |||
| 2684 |
5/8✓ Branch 6 → 7 taken 4 times.
✗ Branch 6 → 213 not taken.
✓ Branch 7 → 8 taken 4 times.
✗ Branch 7 → 10 not taken.
✓ Branch 8 → 9 taken 4 times.
✗ Branch 8 → 213 not taken.
✓ Branch 9 → 10 taken 3 times.
✓ Branch 9 → 11 taken 1 time.
|
4 | const int incr = (vi.IsRGB24() || vi.IsRGB48()) ? 3 : 4; |
| 2685 | |||
| 2686 |
10/22✓ Branch 12 → 13 taken 4 times.
✗ Branch 12 → 213 not taken.
✓ Branch 13 → 14 taken 4 times.
✗ Branch 13 → 22 not taken.
✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 213 not taken.
✓ Branch 15 → 16 taken 4 times.
✗ Branch 15 → 22 not taken.
✓ Branch 16 → 17 taken 4 times.
✗ Branch 16 → 213 not taken.
✓ Branch 17 → 18 taken 3 times.
✓ Branch 17 → 22 taken 1 time.
✓ Branch 18 → 19 taken 3 times.
✗ Branch 18 → 213 not taken.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 22 taken 3 times.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 213 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
✓ Branch 24 → 25 taken 4 times.
✗ Branch 24 → 56 not taken.
|
4 | if (vi.IsRGB24() || vi.IsYUY2() || vi.IsRGB32() || vi.IsRGB48() || vi.IsRGB64()) { |
| 2687 | |||
| 2688 |
1/2✓ Branch 26 → 27 taken 4 times.
✗ Branch 26 → 213 not taken.
|
4 | const BYTE* f1ptr = f1->GetReadPtr(); |
| 2689 |
1/2✓ Branch 28 → 29 taken 4 times.
✗ Branch 28 → 213 not taken.
|
4 | const BYTE* f2ptr = f2->GetReadPtr(); |
| 2690 |
1/2✓ Branch 30 → 31 taken 4 times.
✗ Branch 30 → 213 not taken.
|
4 | const int pitch1 = f1->GetPitch(); |
| 2691 |
1/2✓ Branch 32 → 33 taken 4 times.
✗ Branch 32 → 213 not taken.
|
4 | const int pitch2 = f2->GetPitch(); |
| 2692 |
1/2✓ Branch 34 → 35 taken 4 times.
✗ Branch 34 → 213 not taken.
|
4 | const int rowsize = f1->GetRowSize(); |
| 2693 |
1/2✓ Branch 36 → 37 taken 4 times.
✗ Branch 36 → 213 not taken.
|
4 | const int height = f1->GetHeight(); |
| 2694 | |||
| 2695 | 4 | bytecount = (rowsize / pixelsize) * height * masked_bytes / 4; | |
| 2696 | #ifdef INTEL_INTRINSICS | ||
| 2697 | |||
| 2698 |
6/16✓ Branch 37 → 38 taken 4 times.
✗ Branch 37 → 213 not taken.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 46 taken 1 time.
✓ Branch 40 → 41 taken 3 times.
✗ Branch 40 → 213 not taken.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 3 times.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 46 not taken.
✓ Branch 43 → 44 taken 3 times.
✗ Branch 43 → 213 not taken.
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 50 taken 3 times.
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 50 not taken.
|
8 | if (((vi.IsRGB32() && (rowsize % 16 == 0)) || (vi.IsRGB24() && (rowsize % 12 == 0)) || (vi.IsYUY2() && (rowsize % 16 == 0))) && |
| 2699 |
7/10✓ Branch 38 → 39 taken 1 time.
✓ Branch 38 → 40 taken 3 times.
✓ Branch 46 → 47 taken 1 time.
✗ Branch 46 → 50 not taken.
✓ Branch 47 → 48 taken 1 time.
✗ Branch 47 → 213 not taken.
✓ Branch 48 → 49 taken 1 time.
✗ Branch 48 → 50 not taken.
✓ Branch 51 → 52 taken 1 time.
✓ Branch 51 → 53 taken 3 times.
|
8 | (pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2)) // only for uint8_t (pixelsize==1), todo |
| 2700 | { | ||
| 2701 | |||
| 2702 |
1/2✓ Branch 52 → 90 taken 1 time.
✗ Branch 52 → 213 not taken.
|
1 | compare_sse2(mask, incr, f1ptr, pitch1, f2ptr, pitch2, rowsize, height, SAD, SD, pos_D, neg_D, SSD); |
| 2703 | } | ||
| 2704 | else | ||
| 2705 | #ifdef X86_32 | ||
| 2706 | if (((vi.IsRGB32() && (rowsize % 8 == 0)) || (vi.IsRGB24() && (rowsize % 6 == 0)) || (vi.IsYUY2() && (rowsize % 8 == 0))) && | ||
| 2707 | (pixelsize == 1) && (env->GetCPUFlags() & CPUF_INTEGER_SSE)) // only for uint8_t (pixelsize==1), todo | ||
| 2708 | { | ||
| 2709 | compare_isse(mask, incr, f1ptr, pitch1, f2ptr, pitch2, rowsize, height, SAD, SD, pos_D, neg_D, SSD); | ||
| 2710 | } | ||
| 2711 | else | ||
| 2712 | #endif | ||
| 2713 | #endif | ||
| 2714 | { | ||
| 2715 | |||
| 2716 |
1/2✗ Branch 53 → 54 not taken.
✓ Branch 53 → 55 taken 3 times.
|
3 | if (pixelsize == 1) |
| 2717 | ✗ | compare_c(mask, incr, f1ptr, pitch1, f2ptr, pitch2, rowsize, height, SAD, SD, pos_D, neg_D, SSD); | |
| 2718 | else | ||
| 2719 |
1/2✓ Branch 55 → 90 taken 3 times.
✗ Branch 55 → 213 not taken.
|
3 | compare_uint16_t_c(mask64, incr, f1ptr, pitch1, f2ptr, pitch2, rowsize, height, SAD_64, SD_64, pos_D, neg_D, SSD); |
| 2720 | } | ||
| 2721 | } | ||
| 2722 | else { // Planar | ||
| 2723 | |||
| 2724 | ✗ | int planes_y[4] = { PLANAR_Y, PLANAR_U, PLANAR_V, PLANAR_A }; | |
| 2725 | ✗ | int planes_r[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A }; | |
| 2726 | ✗ | int* planes = (vi.IsYUV() || vi.IsYUVA()) ? planes_y : planes_r; | |
| 2727 | ✗ | for (int p = 0; p < 4; p++) { | |
| 2728 | ✗ | const int plane = planes[p]; | |
| 2729 | |||
| 2730 | ✗ | if (planar_plane & plane) { | |
| 2731 | |||
| 2732 | ✗ | const BYTE* f1ptr = f1->GetReadPtr(plane); | |
| 2733 | ✗ | const BYTE* f2ptr = f2->GetReadPtr(plane); | |
| 2734 | ✗ | const int pitch1 = f1->GetPitch(plane); | |
| 2735 | ✗ | const int pitch2 = f2->GetPitch(plane); | |
| 2736 | ✗ | const int rowsize = f1->GetRowSize(plane); | |
| 2737 | ✗ | const int height = f1->GetHeight(plane); | |
| 2738 | |||
| 2739 | ✗ | bytecount += (rowsize / pixelsize) * height; | |
| 2740 | #ifdef INTEL_INTRINSICS | ||
| 2741 | |||
| 2742 | ✗ | if ((pixelsize == 1) && (rowsize % 16 == 0) && (env->GetCPUFlags() & CPUF_SSE2)) | |
| 2743 | { | ||
| 2744 | ✗ | compare_sse2(mask, incr, f1ptr, pitch1, f2ptr, pitch2, rowsize, height, SAD, SD, pos_D, neg_D, SSD); | |
| 2745 | } | ||
| 2746 | else | ||
| 2747 | #ifdef X86_32 | ||
| 2748 | if ((pixelsize == 1) && (rowsize % 8 == 0) && (env->GetCPUFlags() & CPUF_INTEGER_SSE)) | ||
| 2749 | { | ||
| 2750 | compare_isse(mask, incr, f1ptr, pitch1, f2ptr, pitch2, rowsize, height, SAD, SD, pos_D, neg_D, SSD); | ||
| 2751 | } | ||
| 2752 | else | ||
| 2753 | #endif | ||
| 2754 | #endif | ||
| 2755 | { | ||
| 2756 | |||
| 2757 | ✗ | if (pixelsize == 1) | |
| 2758 | ✗ | compare_planar_c(f1ptr, pitch1, f2ptr, pitch2, rowsize, height, SAD, SD, pos_D, neg_D, SSD); | |
| 2759 | else | ||
| 2760 | ✗ | compare_planar_uint16_t_c(f1ptr, pitch1, f2ptr, pitch2, rowsize, height, SAD_64, SD_64, pos_D, neg_D, SSD); | |
| 2761 | } | ||
| 2762 | } | ||
| 2763 | } | ||
| 2764 | } | ||
| 2765 | |||
| 2766 |
2/2✓ Branch 90 → 91 taken 1 time.
✓ Branch 90 → 92 taken 3 times.
|
4 | double MAD = ((pixelsize==1) ? (double)SAD : (double)SAD_64) / bytecount; |
| 2767 |
2/2✓ Branch 93 → 94 taken 1 time.
✓ Branch 93 → 95 taken 3 times.
|
4 | double MD = ((pixelsize==1) ? (double)SD : (double)SD_64) / bytecount; |
| 2768 |
1/2✗ Branch 96 → 97 not taken.
✓ Branch 96 → 98 taken 4 times.
|
4 | if (SSD == 0.0) SSD = 1.0; |
| 2769 | 4 | const int max_pixel_value = (1 << bits_per_pixel) - 1; | |
| 2770 | 4 | double factor = (double)(max_pixel_value); | |
| 2771 | 4 | double PSNR = 10.0 * log10(bytecount * factor * factor / SSD); | |
| 2772 | |||
| 2773 | 4 | framecount++; | |
| 2774 |
1/2✓ Branch 98 → 99 taken 4 times.
✗ Branch 98 → 100 not taken.
|
4 | if (framecount == 1) { |
| 2775 | 4 | MAD_min = MAD_tot = MAD_max = MAD; | |
| 2776 | 4 | MD_min = MD_tot = MD_max = MD; | |
| 2777 | 4 | PSNR_min = PSNR_tot = PSNR_max = PSNR; | |
| 2778 | 4 | bytecount_overall = double(bytecount); | |
| 2779 | 4 | SSD_overall = SSD; | |
| 2780 | } else { | ||
| 2781 | ✗ | MAD_min = min(MAD_min, MAD); | |
| 2782 | ✗ | MAD_tot += MAD; | |
| 2783 | ✗ | MAD_max = max(MAD_max, MAD); | |
| 2784 | ✗ | MD_min = min(MD_min, MD); | |
| 2785 | ✗ | MD_tot += MD; | |
| 2786 | ✗ | MD_max = max(MD_max, MD); | |
| 2787 | ✗ | PSNR_min = min(PSNR_min, PSNR); | |
| 2788 | ✗ | PSNR_tot += PSNR; | |
| 2789 | ✗ | PSNR_max = max(PSNR_max, PSNR); | |
| 2790 | ✗ | bytecount_overall += double(bytecount); | |
| 2791 | ✗ | SSD_overall += SSD; | |
| 2792 | } | ||
| 2793 | |||
| 2794 |
1/2✓ Branch 107 → 108 taken 4 times.
✗ Branch 107 → 111 not taken.
|
4 | if (log) { |
| 2795 |
2/2✓ Branch 108 → 109 taken 1 time.
✓ Branch 108 → 110 taken 3 times.
|
4 | if (pixelsize == 1) |
| 2796 |
1/2✓ Branch 109 → 205 taken 1 time.
✗ Branch 109 → 213 not taken.
|
1 | fprintf(log,"%6u %8.4f %+9.4f %3d %3d %8.4f\n", (unsigned int)n, MAD, MD, pos_D, neg_D, PSNR); |
| 2797 | else | ||
| 2798 |
1/2✓ Branch 110 → 205 taken 3 times.
✗ Branch 110 → 213 not taken.
|
3 | fprintf(log,"%6u %11.4f %+12.4f %7d %7d %8.4f\n", (unsigned int)n, MAD, MD, pos_D, neg_D, PSNR); |
| 2799 | } else { | ||
| 2800 | ✗ | env->MakeWritable(&f1); | |
| 2801 | ✗ | BYTE* dstp = f1->GetWritePtr(); | |
| 2802 | ✗ | int dst_pitch = f1->GetPitch(); | |
| 2803 | |||
| 2804 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 2805 | HDC hdc = nullptr; | ||
| 2806 | if (use_gdi) | ||
| 2807 | hdc = antialiaser->GetDC(); | ||
| 2808 | if (use_gdi ? hdc != nullptr : current_font != nullptr) | ||
| 2809 | #else | ||
| 2810 | ✗ | if (current_font != nullptr) | |
| 2811 | #endif | ||
| 2812 | { | ||
| 2813 | char text[600]; | ||
| 2814 | ✗ | double PSNR_overall = 10.0 * log10(bytecount_overall * factor * factor / SSD_overall); | |
| 2815 | ✗ | if (pixelsize == 1) | |
| 2816 | ✗ | snprintf(text, sizeof(text), | |
| 2817 | " Frame: %-8u( min / avg / max )\n" | ||
| 2818 | "Mean Abs Dev:%8.4f (%7.3f /%7.3f /%7.3f )\n" | ||
| 2819 | " Mean Dev:%+8.4f (%+7.3f /%+7.3f /%+7.3f )\n" | ||
| 2820 | " Max Pos Dev:%4d \n" | ||
| 2821 | " Max Neg Dev:%4d \n" | ||
| 2822 | " PSNR:%6.2f dB ( %6.2f / %6.2f / %6.2f )\n" | ||
| 2823 | "Overall PSNR:%6.2f dB\n", | ||
| 2824 | n, | ||
| 2825 | ✗ | MAD, MAD_min, MAD_tot / framecount, MD_max, | |
| 2826 | ✗ | MD, MD_min, MD_tot / framecount, MD_max, | |
| 2827 | pos_D, | ||
| 2828 | neg_D, | ||
| 2829 | ✗ | PSNR, PSNR_min, PSNR_tot / framecount, PSNR_max, | |
| 2830 | PSNR_overall | ||
| 2831 | ); | ||
| 2832 | else | ||
| 2833 | ✗ | snprintf(text, sizeof(text), | |
| 2834 | " Frame: %-8u ( min / avg / max )\n" | ||
| 2835 | "Mean Abs Dev:%11.4f (%10.3f /%10.3f /%10.3f )\n" | ||
| 2836 | " Mean Dev:%+11.4f (%+10.3f /%+10.3f /%+10.3f )\n" | ||
| 2837 | " Max Pos Dev:%7d \n" | ||
| 2838 | " Max Neg Dev:%7d \n" | ||
| 2839 | " PSNR:%6.2f dB ( %6.2f / %6.2f / %6.2f )\n" | ||
| 2840 | "Overall PSNR:%6.2f dB\n", | ||
| 2841 | n, | ||
| 2842 | ✗ | MAD, MAD_min, MAD_tot / framecount, MD_max, | |
| 2843 | ✗ | MD, MD_min, MD_tot / framecount, MD_max, | |
| 2844 | pos_D, | ||
| 2845 | neg_D, | ||
| 2846 | ✗ | PSNR, PSNR_min, PSNR_tot / framecount, PSNR_max, | |
| 2847 | PSNR_overall | ||
| 2848 | ); | ||
| 2849 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 2850 | if (use_gdi) { | ||
| 2851 | RECT r = { 32, 16, min((51+(pixelsize==1 ? 0: 12))*67, vi.width * 8), 768 + 128 }; // orig: 3440: 51*67, not enough for 16 bit data | ||
| 2852 | DrawText(hdc, text, -1, &r, 0); | ||
| 2853 | GdiFlush(); | ||
| 2854 | antialiaser->Apply(vi, &f1, dst_pitch); | ||
| 2855 | } else { | ||
| 2856 | bool utf8 = true; | ||
| 2857 | auto s_utf8 = charToUtf8(text, utf8); | ||
| 2858 | SimpleTextOutW_multi(current_font.get(), vi, f1, 2, 1, s_utf8, true, text_color, halo_color, false, 0 /* no align */, 0 /*lsp*/, chromaplacement); | ||
| 2859 | } | ||
| 2860 | #else | ||
| 2861 | ✗ | bool utf8 = true; | |
| 2862 | ✗ | auto s_utf8 = charToUtf8(text, utf8); | |
| 2863 | ✗ | SimpleTextOutW_multi(current_font.get(), vi, f1, 2, 1, s_utf8, true, text_color, halo_color, false, 0 /* no align */, 0 /*lsp*/, chromaplacement); | |
| 2864 | #endif | ||
| 2865 | ✗ | } | |
| 2866 | |||
| 2867 | ✗ | if (show_graph) { | |
| 2868 | // original idea by Marc_FD | ||
| 2869 | // PF remark: show-graph (and file logging) is not for multitask | ||
| 2870 | // psnrs array is instance specific | ||
| 2871 | ✗ | psnrs[n] = min((int)(PSNR + 0.5), 100); | |
| 2872 | ✗ | if (vi.height > 196) { | |
| 2873 | ✗ | if (vi.IsYUY2()) { | |
| 2874 | ✗ | dstp += (vi.height - 1) * dst_pitch; | |
| 2875 | ✗ | for (int y = 0; y <= 100; y++) { | |
| 2876 | ✗ | for (int x = max(0, vi.width - n - 1); x < vi.width; x++) { | |
| 2877 | ✗ | if (y <= psnrs[n - vi.width + 1 + x]) { | |
| 2878 | ✗ | if (y <= psnrs[n - vi.width + 1 + x] - 2) { | |
| 2879 | ✗ | dstp[x << 1] = 16; // Y | |
| 2880 | ✗ | dstp[((x & -1) << 1) + 1] = 0x80; // U | |
| 2881 | ✗ | dstp[((x & -1) << 1) + 3] = 0x80; // V | |
| 2882 | } else { | ||
| 2883 | ✗ | dstp[x << 1] = 235; // Y | |
| 2884 | ✗ | dstp[((x & -1) << 1) + 1] = 0x80; // U | |
| 2885 | ✗ | dstp[((x & -1) << 1) + 3] = 0x80; // V | |
| 2886 | } | ||
| 2887 | } | ||
| 2888 | } // for x | ||
| 2889 | ✗ | dstp -= dst_pitch; | |
| 2890 | } // for y | ||
| 2891 | } | ||
| 2892 | ✗ | else if (vi.IsPlanar()) { | |
| 2893 | ✗ | if (vi.IsPlanarRGB() || vi.IsPlanarRGBA()) | |
| 2894 | { | ||
| 2895 | ✗ | BYTE* dstp_RGBP[3] = { f1->GetWritePtr(PLANAR_G), f1->GetWritePtr(PLANAR_B),f1->GetWritePtr(PLANAR_R) }; | |
| 2896 | ✗ | int dst_pitch_RGBP[3] = { f1->GetPitch(PLANAR_G), f1->GetPitch(PLANAR_B), f1->GetPitch(PLANAR_R) }; | |
| 2897 | |||
| 2898 | ✗ | dstp_RGBP[0] += (vi.height - 1) * dst_pitch_RGBP[0]; | |
| 2899 | ✗ | dstp_RGBP[1] += (vi.height - 1) * dst_pitch_RGBP[1]; | |
| 2900 | ✗ | dstp_RGBP[2] += (vi.height - 1) * dst_pitch_RGBP[2]; | |
| 2901 | ✗ | for (int y = 0; y <= 100; y++) { | |
| 2902 | ✗ | for (int x = max(0, vi.width - n - 1); x < vi.width; x++) { | |
| 2903 | ✗ | if (y <= psnrs[n - vi.width + 1 + x]) { | |
| 2904 | ✗ | if (y <= psnrs[n - vi.width + 1 + x] - 2) { | |
| 2905 | ✗ | if(pixelsize==1) { | |
| 2906 | ✗ | dstp_RGBP[0][x] = 0; | |
| 2907 | ✗ | dstp_RGBP[1][x] = 0; | |
| 2908 | ✗ | dstp_RGBP[2][x] = 0; | |
| 2909 | } else { | ||
| 2910 | ✗ | reinterpret_cast<uint16_t *>(dstp_RGBP[0])[x] = 0; | |
| 2911 | ✗ | reinterpret_cast<uint16_t *>(dstp_RGBP[1])[x] = 0; | |
| 2912 | ✗ | reinterpret_cast<uint16_t *>(dstp_RGBP[2])[x] = 0; | |
| 2913 | } | ||
| 2914 | } else { | ||
| 2915 | ✗ | if(pixelsize==1) { | |
| 2916 | ✗ | dstp_RGBP[0][x] = 0xFF; | |
| 2917 | ✗ | dstp_RGBP[1][x] = 0xFF; | |
| 2918 | ✗ | dstp_RGBP[2][x] = 0xFF; | |
| 2919 | } else { | ||
| 2920 | ✗ | reinterpret_cast<uint16_t *>(dstp_RGBP[0])[x] = max_pixel_value; | |
| 2921 | ✗ | reinterpret_cast<uint16_t *>(dstp_RGBP[1])[x] = max_pixel_value; | |
| 2922 | ✗ | reinterpret_cast<uint16_t *>(dstp_RGBP[2])[x] = max_pixel_value; | |
| 2923 | } | ||
| 2924 | } | ||
| 2925 | } | ||
| 2926 | } // for x | ||
| 2927 | ✗ | dstp_RGBP[0] -= dst_pitch_RGBP[0]; | |
| 2928 | ✗ | dstp_RGBP[1] -= dst_pitch_RGBP[1]; | |
| 2929 | ✗ | dstp_RGBP[2] -= dst_pitch_RGBP[2]; | |
| 2930 | } | ||
| 2931 | } else { | ||
| 2932 | // planar YUV | ||
| 2933 | ✗ | dstp += (vi.height - 1) * dst_pitch; | |
| 2934 | ✗ | const int black = 16 << (bits_per_pixel - 8); | |
| 2935 | ✗ | const int white = 235 << (bits_per_pixel - 8); | |
| 2936 | ✗ | for (int y = 0; y <= 100; y++) { | |
| 2937 | ✗ | for (int x = max(0, vi.width - n - 1); x < vi.width; x++) { | |
| 2938 | ✗ | if (y <= psnrs[n - vi.width + 1 + x]) { | |
| 2939 | ✗ | if (y <= psnrs[n - vi.width + 1 + x] - 2) { | |
| 2940 | ✗ | if(pixelsize==1) | |
| 2941 | ✗ | dstp[x] = 16; // Y | |
| 2942 | else | ||
| 2943 | ✗ | reinterpret_cast<uint16_t *>(dstp)[x] = black; // Y | |
| 2944 | } else { | ||
| 2945 | ✗ | if(pixelsize==1) | |
| 2946 | ✗ | dstp[x] = 235; // Y | |
| 2947 | else | ||
| 2948 | ✗ | reinterpret_cast<uint16_t *>(dstp)[x] = white; // Y | |
| 2949 | } | ||
| 2950 | } | ||
| 2951 | } // for x | ||
| 2952 | ✗ | dstp -= dst_pitch; | |
| 2953 | } | ||
| 2954 | } // for y | ||
| 2955 | } else { // packed RGB 8 or 16 bits | ||
| 2956 | ✗ | for (int y = 0; y <= 100; y++) { | |
| 2957 | ✗ | for (int x = max(0, vi.width - n - 1); x < vi.width; x++) { | |
| 2958 | ✗ | if (y <= psnrs[n - vi.width + 1 + x]) { | |
| 2959 | ✗ | const int xx = x * incr; | |
| 2960 | ✗ | if (y <= psnrs[n - vi.width + 1 + x] -2) { | |
| 2961 | ✗ | if(pixelsize==1) { | |
| 2962 | ✗ | dstp[xx] = 0x00; // B | |
| 2963 | ✗ | dstp[xx + 1] = 0x00; // G | |
| 2964 | ✗ | dstp[xx + 2] = 0x00; // R | |
| 2965 | } | ||
| 2966 | else { | ||
| 2967 | ✗ | reinterpret_cast<uint16_t *>(dstp)[xx] = 0x00; // B | |
| 2968 | ✗ | reinterpret_cast<uint16_t *>(dstp)[xx + 1] = 0x00; // G | |
| 2969 | ✗ | reinterpret_cast<uint16_t *>(dstp)[xx + 2] = 0x00; // R | |
| 2970 | } | ||
| 2971 | } else { | ||
| 2972 | ✗ | if(pixelsize==1) { | |
| 2973 | ✗ | dstp[xx] = 0xFF; // B | |
| 2974 | ✗ | dstp[xx + 1] = 0xFF; // G | |
| 2975 | ✗ | dstp[xx + 2] = 0xFF; // R | |
| 2976 | } | ||
| 2977 | else { | ||
| 2978 | ✗ | reinterpret_cast<uint16_t *>(dstp)[xx] = 0xFFFF; // B | |
| 2979 | ✗ | reinterpret_cast<uint16_t *>(dstp)[xx + 1] = 0xFFFF; // G | |
| 2980 | ✗ | reinterpret_cast<uint16_t *>(dstp)[xx + 2] = 0xFFFF; // R | |
| 2981 | } | ||
| 2982 | } | ||
| 2983 | } | ||
| 2984 | } // for x | ||
| 2985 | ✗ | dstp += dst_pitch; | |
| 2986 | } // for y | ||
| 2987 | } // RGB | ||
| 2988 | } // height > 100 | ||
| 2989 | } // show_graph | ||
| 2990 | } // no logfile | ||
| 2991 | |||
| 2992 | 4 | return f1; | |
| 2993 | 4 | } | |
| 2994 | |||
| 2995 | |||
| 2996 | |||
| 2997 | |||
| 2998 | |||
| 2999 | |||
| 3000 | |||
| 3001 | |||
| 3002 | |||
| 3003 | |||
| 3004 | |||
| 3005 | /************************************ | ||
| 3006 | ******* Helper Functions ****** | ||
| 3007 | ***********************************/ | ||
| 3008 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 3009 | bool GetTextBoundingBox(const char* text, const char* fontname, int size, bool bold, | ||
| 3010 | bool italic, int align, int* width, int* height, bool utf8) | ||
| 3011 | { | ||
| 3012 | HFONT hfont = LoadFont(fontname, size, bold, italic); | ||
| 3013 | if (hfont == NULL) | ||
| 3014 | return false; | ||
| 3015 | HDC hdc = GetDC(NULL); | ||
| 3016 | if (hdc == NULL) | ||
| 3017 | return false; | ||
| 3018 | HFONT hfontDefault = (HFONT)SelectObject(hdc, hfont); | ||
| 3019 | int old_map_mode = SetMapMode(hdc, MM_TEXT); | ||
| 3020 | UINT old_text_align = SetTextAlign(hdc, align); | ||
| 3021 | |||
| 3022 | // Initialize width and height with base value (8 GDI units) | ||
| 3023 | *height = 8; | ||
| 3024 | *width = 8; | ||
| 3025 | |||
| 3026 | bool success = true; | ||
| 3027 | const char* current_text_ptr = text; | ||
| 3028 | |||
| 3029 | // Handle empty string case immediately | ||
| 3030 | if (text == nullptr || *text == '\0') { | ||
| 3031 | // Already initialized to 8, so nothing more to do for empty text | ||
| 3032 | } | ||
| 3033 | else { | ||
| 3034 | // Loop through lines | ||
| 3035 | |||
| 3036 | while (*current_text_ptr != '\0') | ||
| 3037 | { | ||
| 3038 | // Find the end of the current line segment (first CR or LF) | ||
| 3039 | const char* line_end_ptr = current_text_ptr; | ||
| 3040 | while (*line_end_ptr != '\0' && *line_end_ptr != '\r' && *line_end_ptr != '\n') { | ||
| 3041 | line_end_ptr++; | ||
| 3042 | } | ||
| 3043 | |||
| 3044 | const char* next_segment_start; | ||
| 3045 | std::unique_ptr<wchar_t[]> wide_line; // Used for converting a substring | ||
| 3046 | std::string temp_line; | ||
| 3047 | |||
| 3048 | if (*line_end_ptr != '\0') // Found a line break character | ||
| 3049 | { | ||
| 3050 | // Extract the current line segment | ||
| 3051 | temp_line.assign(current_text_ptr, (size_t)(line_end_ptr - current_text_ptr)); | ||
| 3052 | wide_line = utf8 ? Utf8ToWideChar(temp_line.c_str()) : AnsiToWideChar(temp_line.c_str()); | ||
| 3053 | |||
| 3054 | // Advance pointer past the line break(s) | ||
| 3055 | next_segment_start = line_end_ptr; | ||
| 3056 | if (*next_segment_start == '\r' && *(next_segment_start + 1) == '\n') { | ||
| 3057 | next_segment_start += 2; | ||
| 3058 | } | ||
| 3059 | else { | ||
| 3060 | next_segment_start += 1; | ||
| 3061 | } | ||
| 3062 | } | ||
| 3063 | else // End of string | ||
| 3064 | { | ||
| 3065 | // Process the rest of the string as the last line | ||
| 3066 | wide_line = utf8 ? Utf8ToWideChar(current_text_ptr) : AnsiToWideChar(current_text_ptr); | ||
| 3067 | next_segment_start = current_text_ptr + strlen(current_text_ptr); | ||
| 3068 | } | ||
| 3069 | |||
| 3070 | if (!wide_line) { | ||
| 3071 | success = false; | ||
| 3072 | break; | ||
| 3073 | } | ||
| 3074 | |||
| 3075 | RECT r = { 0, 0, 0, 0 }; | ||
| 3076 | // Use DrawTextW for wide characters with DT_CALCRECT and DT_SINGLELINE | ||
| 3077 | success = (DrawTextW(hdc, wide_line.get(), (int)(wcslen(wide_line.get())), &r, DT_CALCRECT | DT_NOPREFIX | DT_SINGLELINE) != 0); | ||
| 3078 | |||
| 3079 | if (!success) | ||
| 3080 | break; | ||
| 3081 | |||
| 3082 | // +8 GDI units to the right side (width) as well | ||
| 3083 | *width = std::max(*width, (int)r.right + 8); // Update max width encountered | ||
| 3084 | *height += r.bottom; // Add this line's height | ||
| 3085 | |||
| 3086 | if (*next_segment_start != '\0') | ||
| 3087 | current_text_ptr = next_segment_start;// Move past the newline character | ||
| 3088 | else | ||
| 3089 | break; // No more newlines, end of text | ||
| 3090 | } | ||
| 3091 | } | ||
| 3092 | |||
| 3093 | // Clean up GDI objects | ||
| 3094 | SetTextAlign(hdc, old_text_align); | ||
| 3095 | SetMapMode(hdc, old_map_mode); | ||
| 3096 | SelectObject(hdc, hfontDefault); | ||
| 3097 | DeleteObject(hfont); | ||
| 3098 | ReleaseDC(NULL, hdc); | ||
| 3099 | |||
| 3100 | return success; | ||
| 3101 | } | ||
| 3102 | #endif | ||
| 3103 | |||
| 3104 | ✗ | bool GetTextBoundingBoxFixed(const char* text, const char* fontname, int size, bool bold, | |
| 3105 | bool italic, int align, int& width, int& height, bool utf8) | ||
| 3106 | { | ||
| 3107 | ✗ | std::unique_ptr<BitmapFont> current_font; | |
| 3108 | /* | ||
| 3109 | if (*font_filename) { | ||
| 3110 | // external font file | ||
| 3111 | const bool debugSave = false; | ||
| 3112 | current_font = GetBitmapFont(0, font_filename, false, debugSave); // 0: size n/a | ||
| 3113 | if (current_font == nullptr) | ||
| 3114 | env->ThrowError("SimpleText: file %s not found or unknown file format", font_filename); | ||
| 3115 | } | ||
| 3116 | else | ||
| 3117 | */ | ||
| 3118 | { | ||
| 3119 | // internal font | ||
| 3120 | ✗ | if (fontname) { | |
| 3121 | ✗ | current_font = GetBitmapFont(size, fontname, bold, false); // 12, "Terminus" | |
| 3122 | ✗ | if (current_font == nullptr) | |
| 3123 | ✗ | return false; // ("internal font name %s in size %d not found", fontname, size | |
| 3124 | } | ||
| 3125 | else { | ||
| 3126 | // size | ||
| 3127 | ✗ | current_font = GetBitmapFont(size, "", bold, false); // 12, "" | |
| 3128 | ✗ | if (current_font == nullptr) | |
| 3129 | ✗ | return false; // "fixed font size %d not found", size | |
| 3130 | } | ||
| 3131 | } | ||
| 3132 | |||
| 3133 | ✗ | size_t max_width = 1; | |
| 3134 | ✗ | height = 1; | |
| 3135 | |||
| 3136 | // make list governed by LF separator | ||
| 3137 | ✗ | std::string temp; | |
| 3138 | ✗ | std::stringstream ss(text); | |
| 3139 | ✗ | while (std::getline(ss, temp, '\n')) { | |
| 3140 | // does not recognize combined unicode sequences, | ||
| 3141 | // e.g. U: is len=2 and not len=1 like Ü | ||
| 3142 | |||
| 3143 | // We no longer assume fixed width for all chars in BDF, must calculate width by adding each characters' real width | ||
| 3144 | // max_width = std::max(max_width, real_len * current_font->global_bbx.width); | ||
| 3145 | // cannot do simple len * FONT_WIDTH, because characters can have different widths | ||
| 3146 | |||
| 3147 | ✗ | std::string s_utf8 = charToUtf8(temp.c_str(), utf8); | |
| 3148 | // map an utf8 string to a sequence of character map indexes | ||
| 3149 | ✗ | auto s_remapped = current_font->remap(s_utf8); // array of font table indexes | |
| 3150 | ✗ | size_t total_width = 0; | |
| 3151 | ✗ | for (int i = 0; i < (int)s_remapped.size(); i++) { | |
| 3152 | // bbx_array is the array of bounding boxes for each character | ||
| 3153 | // s[i] is the index to the fontbitmap array | ||
| 3154 | ✗ | total_width += current_font->bbx_array[s_remapped[i]].width; | |
| 3155 | } | ||
| 3156 | ✗ | max_width = std::max(max_width, total_width); | |
| 3157 | ✗ | height += current_font->global_bbx.height; | |
| 3158 | ✗ | } | |
| 3159 | |||
| 3160 | ✗ | width = (int)max_width; | |
| 3161 | |||
| 3162 | ✗ | return true; | |
| 3163 | ✗ | } | |
| 3164 | |||
| 3165 | // old ApplyMessage with an extra utf8 parameter | ||
| 3166 | 1 | void ApplyMessageEx(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size, | |
| 3167 | int textcolor, int halocolor, int bgcolor, bool utf8, IScriptEnvironment* env) | ||
| 3168 | { | ||
| 3169 | AVS_UNUSED(bgcolor); | ||
| 3170 | AVS_UNUSED(env); | ||
| 3171 |
5/10✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 54 not taken.
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 6 not taken.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 54 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1 time.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 12 taken 1 time.
|
1 | if (vi.IsYUV() || vi.IsYUVA()) { |
| 3172 | ✗ | textcolor = RGB2YUV_Rec601(textcolor); | |
| 3173 | ✗ | halocolor = RGB2YUV_Rec601(halocolor); | |
| 3174 | } | ||
| 3175 | |||
| 3176 | 1 | const bool bold = true; | |
| 3177 | 1 | const bool italic = false; | |
| 3178 | 1 | const bool noaa = false; | |
| 3179 | |||
| 3180 | int chromaplacement; | ||
| 3181 | |||
| 3182 | // SD: Up to 720x576 (PAL/NTSC standard definition) | ||
| 3183 |
1/4✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 1 time.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
|
1 | if (vi.width <= 720 || vi.height <= 576) { |
| 3184 | 1 | chromaplacement = ChromaLocation_e::AVS_CHROMA_CENTER; | |
| 3185 | } | ||
| 3186 | // HD: Up to 1920x1080 (Full HD) | ||
| 3187 | ✗ | else if (vi.width <= 1920 || vi.height <= 1080) { | |
| 3188 | ✗ | chromaplacement = ChromaLocation_e::AVS_CHROMA_LEFT; | |
| 3189 | } | ||
| 3190 | // UHD / 4K and above | ||
| 3191 | else { | ||
| 3192 | ✗ | chromaplacement = ChromaLocation_e::AVS_CHROMA_TOP_LEFT; | |
| 3193 | } | ||
| 3194 | |||
| 3195 | #if defined(AVS_WINDOWS) && !defined(NO_WIN_GDI) | ||
| 3196 | const int64_t cpuFlags = env->GetCPUFlagsEx(); | ||
| 3197 | Antialiaser antialiaser(vi.width, vi.height, "Arial", size, textcolor, halocolor, bold, italic, noaa, cpuFlags, chromaplacement); | ||
| 3198 | HDC hdcAntialias = antialiaser.GetDC(); | ||
| 3199 | if (hdcAntialias) | ||
| 3200 | { | ||
| 3201 | RECT r = { 4 * 8, 4 * 8, vi.width * 8, vi.height * 8 }; | ||
| 3202 | |||
| 3203 | auto utf8Message = utf8 ? Utf8ToWideChar(message) : AnsiToWideChar(message); | ||
| 3204 | DrawTextW(hdcAntialias, utf8Message.get(), (int)wcslen(utf8Message.get()), &r, DT_NOPREFIX | DT_CENTER); | ||
| 3205 | |||
| 3206 | GdiFlush(); | ||
| 3207 | antialiaser.Apply(vi, frame, (*frame)->GetPitch()); | ||
| 3208 | } | ||
| 3209 | #else | ||
| 3210 | 1 | std::unique_ptr<BitmapFont> current_font; | |
| 3211 | |||
| 3212 | 1 | size = size / 8; // size comes in GDI units (*8) | |
| 3213 | |||
| 3214 | // internal font | ||
| 3215 |
1/2✓ Branch 19 → 20 taken 1 time.
✗ Branch 19 → 47 not taken.
|
1 | current_font = GetBitmapFont(size, "Terminus", bold, false); |
| 3216 |
1/2✗ Branch 23 → 24 not taken.
✓ Branch 23 → 36 taken 1 time.
|
1 | if (current_font == nullptr) |
| 3217 | { | ||
| 3218 | // size | ||
| 3219 | ✗ | current_font = GetBitmapFont(size, "", bold, false); | |
| 3220 | ✗ | if (current_font == nullptr) | |
| 3221 | ✗ | current_font = GetBitmapFont(size, "", !bold, false); | |
| 3222 | ✗ | if (current_font == nullptr) | |
| 3223 | ✗ | return; | |
| 3224 | } | ||
| 3225 | |||
| 3226 | // AVS_POSIX: utf8 is always true | ||
| 3227 |
1/2✓ Branch 36 → 37 taken 1 time.
✗ Branch 36 → 52 not taken.
|
1 | std::string s_utf8 = charToUtf8(message, utf8); |
| 3228 | |||
| 3229 | 1 | int align = 7; | |
| 3230 | 1 | int lsp = 0; | |
| 3231 | 1 | int x = 4; | |
| 3232 | 1 | int y = 4; | |
| 3233 | |||
| 3234 |
1/2✓ Branch 38 → 39 taken 1 time.
✗ Branch 38 → 50 not taken.
|
1 | SimpleTextOutW_multi(current_font.get(), vi, *frame, x, y, s_utf8, false, textcolor, halocolor, true, align, lsp, chromaplacement); |
| 3235 | |||
| 3236 | #endif | ||
| 3237 |
1/2✓ Branch 42 → 43 taken 1 time.
✗ Branch 42 → 45 not taken.
|
1 | } |
| 3238 | |||
| 3239 | ✗ | void ApplyMessage(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size, | |
| 3240 | int textcolor, int halocolor, int bgcolor, IScriptEnvironment* env) { | ||
| 3241 | // Simply call ApplyMessageEx with utf8=false | ||
| 3242 | ✗ | ApplyMessageEx(frame, vi, message, size, textcolor, halocolor, bgcolor, false /*utf8*/, env); | |
| 3243 | ✗ | } | |
| 3244 | |||
| 3245 |