|
| 1 | +/****************************************************************************** |
| 2 | +Circles filter plugin for Vapoursynth+ 32/64 bit version |
| 3 | +Circles draws concentric circles with given origin coordinates and marks in bold specified diameter. |
| 4 | + Thread agnostic (operates under multi thread mode) |
| 5 | + Author V.C.Mohan |
| 6 | +
|
| 7 | + 10 Sep 2021 |
| 8 | +Copyright (C) <2021> <V.C.Mohan> |
| 9 | +
|
| 10 | + This program is free software: you can redistribute it and/or modify |
| 11 | + it under the terms of the GNU General Public License as published by |
| 12 | + the Free Software Foundation, version 3 of the License. |
| 13 | +
|
| 14 | + This program is distributed in the hope that it will be useful, |
| 15 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | + GNU General Public License for more details. |
| 18 | +
|
| 19 | + A copy of the GNU General Public License is at |
| 20 | + see <http://www.gnu.org/licenses/>. |
| 21 | +
|
| 22 | + For details of how to contact author see <http://www.avisynth.nl/users/vcmohan/vcmohan.html> |
| 23 | +
|
| 24 | +
|
| 25 | +********************************************************************************/ |
| 26 | + |
| 27 | +//#include "windows.h" |
| 28 | +//#include <stdint.h>const finc* sp, |
| 29 | +// #define _USE_MATH_DEFINES |
| 30 | +//#include "math.h" |
| 31 | +//#include "InterpolationPack.h" |
| 32 | +// |
| 33 | +//#include "VapourSynth.h" |
| 34 | +// |
| 35 | + |
| 36 | +//------------------------------------------------------------------------- |
| 37 | +typedef struct { |
| 38 | + VSNodeRef* node; |
| 39 | + const VSVideoInfo* vi; |
| 40 | + |
| 41 | + // circles origin coordinates |
| 42 | + int origin_y; |
| 43 | + int origin_x; |
| 44 | + int dots; // dot density 1 for 16, 2 for 12, 3 for 8, 4 for 4. |
| 45 | + float dim; // imaged dimmed to this level |
| 46 | + int fdia; // circle with this diameter will be high lighted |
| 47 | + int cint; // interval between circles |
| 48 | + int ddensity; // pixel interval of dots of |
| 49 | + unsigned char col[16]; // color components for fill |
| 50 | + unsigned char bgr[3]; |
| 51 | +} CirclesData; |
| 52 | + |
| 53 | + |
| 54 | + |
| 55 | + |
| 56 | +/*-------------------------------------------------- |
| 57 | + * The following is the implementation |
| 58 | + * of the defined functions. |
| 59 | + --------------------------------------------------*/ |
| 60 | + //Here is the acutal constructor code used |
| 61 | + |
| 62 | +static void VS_CC circlesInit(VSMap* in, VSMap* out, void** instanceData, VSNode* node, VSCore* core, const VSAPI* vsapi) |
| 63 | +{ |
| 64 | + CirclesData* d = (CirclesData*)*instanceData; |
| 65 | + vsapi->setVideoInfo(d->vi, 1, node); |
| 66 | + |
| 67 | + int ht = d->vi->height; |
| 68 | + int wd = d->vi->width; |
| 69 | + const VSFormat* fi = d->vi->format; |
| 70 | + int nbytes = fi->bytesPerSample; |
| 71 | + int nbits = fi->bitsPerSample; |
| 72 | + |
| 73 | + d->ddensity = (5 - d->dots) * 16; |
| 74 | + //uint8_t bgr[] = { 0,0,0 }; |
| 75 | + uint8_t yuv[3]; |
| 76 | + BGR8_YUV(yuv, d->bgr, 8); |
| 77 | + uint8_t* bgr = d->bgr; |
| 78 | + |
| 79 | + for (int k = 0; k < 3; k++) |
| 80 | + { |
| 81 | + if (nbytes == 1) |
| 82 | + { |
| 83 | + if (fi->colorFamily == cmRGB) |
| 84 | + d->col[k] = bgr[k]; |
| 85 | + else |
| 86 | + d->col[k] = yuv[k]; |
| 87 | + } |
| 88 | + else if (nbytes == 2) |
| 89 | + { |
| 90 | + if (fi->colorFamily == cmRGB) |
| 91 | + *((uint16_t*)d->col + k) = (uint16_t)(bgr[k] << (nbits - 8)); |
| 92 | + else |
| 93 | + *((uint16_t*)d->col + k) = (uint16_t)(yuv[k] << (nbits - 8)); |
| 94 | + } |
| 95 | + else // float |
| 96 | + { |
| 97 | + if (fi->colorFamily == cmRGB) |
| 98 | + *((float*)d->col + k) = (float)(bgr[k] / 255.0f); |
| 99 | + else |
| 100 | + { |
| 101 | + if (k == 0) |
| 102 | + *((float*)d->col + k) = (float)((yuv[k] - 16) / 220.0f); |
| 103 | + else |
| 104 | + *((float*)d->col + k) = (float)((yuv[k] - 128) / 220.0f); |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + } |
| 109 | +} |
| 110 | + |
| 111 | +//------------------------------------------------------------------------------------------------ |
| 112 | + |
| 113 | +static const VSFrameRef* VS_CC circlesGetFrame(int n, int activationReason, void** instanceData, |
| 114 | + void** frameData, VSFrameContext* frameCtx, VSCore* core, const VSAPI* vsapi) |
| 115 | +{ |
| 116 | + CirclesData* d = (CirclesData*)*instanceData; |
| 117 | + |
| 118 | + if (activationReason == arInitial) |
| 119 | + { |
| 120 | + vsapi->requestFrameFilter(n, d->node, frameCtx); |
| 121 | + } |
| 122 | + else if (activationReason == arAllFramesReady) |
| 123 | + { |
| 124 | + const VSFrameRef* src = vsapi->getFrameFilter(n, d->node, frameCtx); |
| 125 | + VSFrameRef* dst; |
| 126 | + const VSFormat* fi = d->vi->format; |
| 127 | + int height = vsapi->getFrameHeight(src, 0); |
| 128 | + int width = vsapi->getFrameWidth(src, 0); |
| 129 | + int nbits = fi->bitsPerSample; |
| 130 | + int nbytes = fi->bytesPerSample; |
| 131 | + //will not process A plane |
| 132 | + int np = fi->numPlanes > 3 ? 3 : fi->numPlanes; |
| 133 | + |
| 134 | + int kb = 1; |
| 135 | + |
| 136 | + // get src on which dots will be overlain |
| 137 | + dst = vsapi->copyFrame(src, core); |
| 138 | + |
| 139 | + int frsq = d->fdia * d->fdia / 4; |
| 140 | + |
| 141 | + for (int p = 0; p < np; p++) |
| 142 | + { |
| 143 | + |
| 144 | + const uint8_t* sp = vsapi->getReadPtr(src, p); |
| 145 | + uint8_t* dp = vsapi->getWritePtr(dst, p); |
| 146 | + int pitch = vsapi->getStride(src, p) / nbytes; |
| 147 | + |
| 148 | + if ( fi->colorFamily == cmRGB) |
| 149 | + { |
| 150 | + if (nbytes == 1) |
| 151 | + dimplaneRGB(dp,sp, pitch, width, height, d->dim); |
| 152 | + else if (nbytes == 2) |
| 153 | + dimplaneRGB((uint16_t*)dp, (uint16_t*)sp, pitch, width, height, d->dim); |
| 154 | + else if (nbytes == 4) |
| 155 | + dimplaneRGB((float*)dp, (float *)sp, pitch, width, height, d->dim); |
| 156 | + } |
| 157 | + else if (p == 0 && fi->colorFamily == cmYUV) |
| 158 | + { |
| 159 | + if (nbytes == 1) |
| 160 | + { |
| 161 | + uint8_t limit = 16; |
| 162 | + dimplaneYUV(dp, sp, pitch, width, height, d->dim, limit); |
| 163 | + } |
| 164 | + else if (nbytes == 2) |
| 165 | + { |
| 166 | + uint16_t limit = (uint16_t)(16 << (nbits - 8)); |
| 167 | + dimplaneYUV((uint16_t*)dp, (uint16_t*)sp, pitch, width, height, d->dim, limit); |
| 168 | + } |
| 169 | + else if (nbytes == 4) |
| 170 | + { |
| 171 | + float limit = 0.0f; |
| 172 | + dimplaneYUV((float*)dp, (float*)sp, pitch, width, height, d->dim, limit); |
| 173 | + } |
| 174 | + } |
| 175 | + int fdmax = VSMAX(VSMAX(abs(d->origin_y - height), abs(d->origin_y)), VSMAX(abs(d->origin_x - width), abs(d->origin_x))) ; |
| 176 | + // draw concentric circles |
| 177 | + for (int fd = 0; fd <= fdmax; fd += d->cint) |
| 178 | + { |
| 179 | + int inc = fd == 0 ? 1 : 6 - d->dots; |
| 180 | + int frad = fd == 0 ? d->fdia / 2 : fd; |
| 181 | + |
| 182 | + for (int alfa = 1; alfa < 90; alfa += inc) |
| 183 | + { |
| 184 | + double cosalfa = cos(alfa * M_PI / 180); |
| 185 | + double sinalfa = sin(alfa * M_PI / 180); |
| 186 | + |
| 187 | + int x = (int)(frad * cosalfa); |
| 188 | + int y = (int)(frad * sinalfa); |
| 189 | + |
| 190 | + for (int i = -1; i < 2; i += 2) |
| 191 | + { |
| 192 | + for (int j = -1; j < 2; j += 2) |
| 193 | + { |
| 194 | + int w = d->origin_x + x * j; |
| 195 | + int h = d->origin_y + y * i; |
| 196 | + if (w >= 0 && w < width && h >= 0 && h < height) |
| 197 | + { |
| 198 | + if (nbytes == 1) |
| 199 | + *(dp + h * pitch + w) = d->col[p]; |
| 200 | + |
| 201 | + else if (nbytes == 2) |
| 202 | + *((uint16_t*)dp + h * pitch + w) = *((uint16_t*)d->col + p); |
| 203 | + else if (nbytes == 4) |
| 204 | + *((float*)dp + h * pitch + w) = *((float*)d->col + p); |
| 205 | + } |
| 206 | + } |
| 207 | + } |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + } |
| 212 | + vsapi->freeFrame(src); |
| 213 | + return dst; |
| 214 | + } |
| 215 | + return 0; |
| 216 | +} |
| 217 | + |
| 218 | +/***************************************************************/ |
| 219 | +static void VS_CC circlesFree(void* instanceData, VSCore* core, const VSAPI* vsapi) |
| 220 | +{ |
| 221 | + CirclesData* d = (CirclesData*)instanceData; |
| 222 | + vsapi->freeNode(d->node); |
| 223 | + |
| 224 | + free(d); |
| 225 | +} |
| 226 | + |
| 227 | +static void VS_CC circlesCreate(const VSMap* in, VSMap* out, void* userData, |
| 228 | + VSCore* core, const VSAPI* vsapi) |
| 229 | +{ |
| 230 | + CirclesData d; |
| 231 | + CirclesData* data; |
| 232 | + int err; |
| 233 | + int temp; |
| 234 | + |
| 235 | + // Get a clip reference from the input arguments. This must be freed later. |
| 236 | + d.node = vsapi->propGetNode(in, "clip", 0, 0); |
| 237 | + d.vi = vsapi->getVideoInfo(d.node); |
| 238 | + |
| 239 | + // In this first version we only want to handle 8bit integer formats. Note that |
| 240 | + // vi->format can be 0 if the input clip can change format midstream. |
| 241 | + if (!isConstantFormat(d.vi) || d.vi->width == 0 || d.vi->height == 0 |
| 242 | + || (d.vi->format->colorFamily != cmYUV && d.vi->format->colorFamily != cmGray |
| 243 | + && d.vi->format->colorFamily != cmRGB)) |
| 244 | + { |
| 245 | + vsapi->setError(out, "Circles: only RGB, Yuv or Gray color constant formats and const frame dimensions input supported"); |
| 246 | + vsapi->freeNode(d.node); |
| 247 | + return; |
| 248 | + } |
| 249 | + |
| 250 | + |
| 251 | + if (d.vi->format->sampleType == stFloat && d.vi->format->bitsPerSample == 16) |
| 252 | + { |
| 253 | + vsapi->setError(out, "Circles: half float input not allowed."); |
| 254 | + vsapi->freeNode(d.node); |
| 255 | + return; |
| 256 | + } |
| 257 | + |
| 258 | + // If a property read fails for some reason (index out of bounds/wrong type) |
| 259 | + // then err will have flags set to indicate why and 0 will be returned. This |
| 260 | + // can be very useful to know when having optional arguments. Since we have |
| 261 | + // strict checking because of what we wrote in the argument string, the only |
| 262 | + // reason this could fail is when the value wasn't set by the user. |
| 263 | + // And when it's not set we want it to default to enabled. |
| 264 | + |
| 265 | + d.origin_x = int64ToIntS(vsapi->propGetInt(in, "xo", 0, &err)); |
| 266 | + if (err) |
| 267 | + d.origin_x = d.vi->width / 2; |
| 268 | + |
| 269 | + d.origin_y = int64ToIntS(vsapi->propGetInt(in, "yo", 0, &err)); |
| 270 | + if (err) |
| 271 | + d.origin_y = d.vi->height / 2; |
| 272 | + |
| 273 | + d.fdia = int64ToIntS(vsapi->propGetInt(in, "frad", 0, &err)) * 2; |
| 274 | + if (err) |
| 275 | + d.fdia = (d.vi->height > d.vi->width ? d.vi->width : d.vi->height) * 2; |
| 276 | + |
| 277 | + else if (d.fdia < 128 ) |
| 278 | + { |
| 279 | + vsapi->setError(out, "Circles: frad must be at least 64 "); |
| 280 | + vsapi->freeNode(d.node); |
| 281 | + return; |
| 282 | + } |
| 283 | + if ( (d.origin_x + d.fdia < 0 || d.origin_x - d.fdia > d.vi->width) |
| 284 | + || (d.origin_y + d.fdia < 0 || d.origin_y - d.fdia > d.vi->height)) |
| 285 | + { |
| 286 | + vsapi->setError(out, "Circles: fdia and origin takes the fisheye image outside frame "); |
| 287 | + vsapi->freeNode(d.node); |
| 288 | + return; |
| 289 | + } |
| 290 | + d.cint = int64ToIntS(vsapi->propGetInt(in, "cint", 0, &err)); |
| 291 | + if (err) |
| 292 | + d.cint = 50; |
| 293 | + |
| 294 | + else if (d.cint < 10 || d.cint > VSMAX(d.vi->width, d.vi->height) / 2 ) |
| 295 | + { |
| 296 | + vsapi->setError(out, "Circles: cint must be at least 10 and not more than half of max dimension of frame"); |
| 297 | + vsapi->freeNode(d.node); |
| 298 | + return; |
| 299 | + } |
| 300 | + |
| 301 | + d.dots = int64ToIntS(vsapi->propGetInt(in, "dots", 0, &err)); |
| 302 | + if (err) |
| 303 | + d.dots = 2; |
| 304 | + else if (d.dots < 1 || d.dots > 4) |
| 305 | + { |
| 306 | + vsapi->setError(out, "Circles: dots must be 1 to 4 only "); |
| 307 | + vsapi->freeNode(d.node); |
| 308 | + return; |
| 309 | + } |
| 310 | + |
| 311 | + d.dim = (float)(1.0 - vsapi->propGetFloat(in, "dim", 0, &err)); |
| 312 | + if (err) |
| 313 | + d.dim = 0.25f; |
| 314 | + if (d.dim < 0.0f || d.dim > 1.0f) |
| 315 | + { |
| 316 | + vsapi->setError(out, "Circles: dim must be from 0 to 1.0 only "); |
| 317 | + vsapi->freeNode(d.node); |
| 318 | + return; |
| 319 | + } |
| 320 | + for (int i = 0; i < 3; i++) |
| 321 | + { |
| 322 | + temp = int64ToIntS(vsapi->propGetInt(in, "rgb", 0, &err)); |
| 323 | + if (err) |
| 324 | + d.bgr[2 - i] = (uint8_t)255; |
| 325 | + else if (temp < 0 || temp > 255) |
| 326 | + { |
| 327 | + vsapi->setError(out, "Circles: rgb array values must be from 0 to 255 only "); |
| 328 | + vsapi->freeNode(d.node); |
| 329 | + return; |
| 330 | + } |
| 331 | + else d.bgr[2 - i] = (uint8_t)temp; |
| 332 | + } |
| 333 | + |
| 334 | + // I usually keep the filter data struct on the stack and don't allocate it |
| 335 | +// until all the input validation is done. |
| 336 | + data = (CirclesData*)malloc(sizeof(d)); |
| 337 | + *data = d; |
| 338 | + |
| 339 | + vsapi->createFilter(in, out, "Circles", circlesInit, circlesGetFrame, circlesFree, fmParallel, 0, data, core); |
| 340 | + |
| 341 | +} |
| 342 | + |
| 343 | +// registerFunc("Circles", "clip:clip;xo:int:opt;yo:int:opt;fdia:int:opt;cint:int:opt;dots:int:opt;rgb:int[]:opt;dim:float:opt;", circlesCreate, 0, plugin); |
| 344 | + |
| 345 | + |
0 commit comments