)]}'
{
  "commit": "c9def552ab032f7525e2844b0dc2f3f3fb294e8b",
  "tree": "8e22ece4b46ad5aab64c9002b9525a7ce922aa41",
  "parents": [
    "d0f00ac9c7bbfa3239c7bf462e4439b62b01e833"
  ],
  "author": {
    "name": "Jeroen Bobbeldijk",
    "email": "jerbob92@gmail.com",
    "time": "Mon Aug 24 12:51:01 2026 -0700"
  },
  "committer": {
    "name": "pdfium-scoped@luci-project-accounts.iam.gserviceaccount.com",
    "email": "pdfium-scoped@luci-project-accounts.iam.gserviceaccount.com",
    "time": "Mon Aug 24 12:51:01 2026 -0700"
  },
  "message": "Speed up CStretchEngine: Zip() the tap loops, interchange StretchVert\n\nThe inner loops read every interpolation weight through\nGetWeightForPosition(), which CHECKs twice per tap, and every source\nsample through a checked span access; StretchVert() additionally built\na checked subspan per tap per destination column. These loops run\ntaps x channels x pixels times per stretched image. The compiler\ncannot elide the checks, because the checked offsets are derived\ninduction variables rather than the loops\u0027 own.\n\nRestructure so the checks are unnecessary:\n\n- PixelWeight replaces GetWeightForPosition() with GetWeights(),\n  returning a correctly-sized span over its trailing array, and adds\n  GetSrcStart(). GetWeights() is the only UNSAFE_BUFFERS() this CL\n  adds, justified by the allocation invariant, and it becomes the only\n  way the tap loops read weights_. The write-side accessors keep the\n  UNSAFE_BUFFERS() they already had.\n- ContinueStretchHorz() walks each column\u0027s weights and source window\n  with fxcrt::Zip(), comparing bounds once per column. For the \u003e8bpp\n  cases the source stride is structurally 3 or 4, so dispatching on it\n  gives each branch a compile-time pixel size via reinterpret_span().\n- StretchVert() is loop-interchanged so taps are the outer loop and\n  each tap reads one contiguous row into a row-wide accumulator,\n  replacing the strided column-major walk. Per byte the taps are still\n  summed in tap order.\n\nZip()ing the horizontal loops lets them vectorize but adds fixed setup\nper destination column, and most columns take only one or two taps:\none is no scaling in that axis, two is bilinear. At that trip count\nthe setup dominates, so the two hottest cases unroll one and two taps,\nsummed in the same order, and fall through to the Zip() loop\notherwise.\n\nThe vertical accumulator is allocated once outside the row loop and\nreused, so a row whose first tap writes every element needs no\nzeroing: it is allocated uninitialised and the first tap assigns\nrather than accumulates, which removes a full-width pass per row. A\nrow with no taps writes nothing and would otherwise repeat the row\nabove, so that case zeroes the accumulator explicitly.\n\nGetSrcStart() hands its value straight to span indexing, so\nSetStartEnd() now CHECKs that the start position is non-negative. The\narea-average branch therefore has to clamp the position it stores for\nan empty source range, and three tests cover the geometries that\nreach it: EmptySourceRangeDownscale,\nMirroredDestinationEmptySourceClip and EmptySourceClipAtRightEdge. A\nfourth, EmptySourceRangeInVerticalPass, covers an empty tap range\nreaching StretchVert(), which the other three cannot:\nStartStretchHorz() rejects a zero-height source clip, so that path\nneeds a mirrored destination whose clip box straddles the destination\nedge.\n\nBug: 549523416\nChange-Id: Ifecc733071c148a7f67c2a5904be0510bdfe9c64\nReviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/155550\nCommit-Queue: Lei Zhang \u003cthestig@chromium.org\u003e\nReviewed-by: Lei Zhang \u003cthestig@chromium.org\u003e\nReviewed-by: Tom Sepez \u003ctsepez@chromium.org\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "f808501f140d1eb3b427134e3ce81153b90be2d1",
      "old_mode": 33188,
      "old_path": "AUTHORS",
      "new_id": "9fa8a89ac0bd6502bc688dffe507fd7d2a25d4d3",
      "new_mode": 33188,
      "new_path": "AUTHORS"
    },
    {
      "type": "modify",
      "old_id": "10622154f97bd2a6808f0f0ec1d0a00c49e1b94c",
      "old_mode": 33188,
      "old_path": "core/fxge/dib/cstretchengine.cpp",
      "new_id": "fb26b633fdcb0d24c8b2e96183ba137b5fd50861",
      "new_mode": 33188,
      "new_path": "core/fxge/dib/cstretchengine.cpp"
    },
    {
      "type": "modify",
      "old_id": "e56411bd99e46c2f7cdedc36550201e616b15de7",
      "old_mode": 33188,
      "old_path": "core/fxge/dib/cstretchengine.h",
      "new_id": "dd699f956d64746a30495fc1e30f789eb9955d7e",
      "new_mode": 33188,
      "new_path": "core/fxge/dib/cstretchengine.h"
    },
    {
      "type": "modify",
      "old_id": "d15965bba1cb4e42edba484fa9658f8863411510",
      "old_mode": 33188,
      "old_path": "core/fxge/dib/cstretchengine_unittest.cpp",
      "new_id": "9312c5f3dba4f62e7acdfdf317148dc99623c2ac",
      "new_mode": 33188,
      "new_path": "core/fxge/dib/cstretchengine_unittest.cpp"
    }
  ]
}
