Skip to content

Conversation

@donmccurdy
Copy link
Member

@donmccurdy donmccurdy commented Dec 4, 2025

Description

Revival of @javagl's #11747. One difference from the original PR, I've aimed to replace the custom measureText implementation with as little divergence from the current results as possible. So I've intentionally not tried to fix #10649 here. Along the same lines, I'm rounding some metrics to the nearest pixel to match the previous behavior. This helps with the test case shown below.

Issue number and link

Testing plan

Primarily, I've tested the PR by programmatically comparing output metrics with the previous implementation, for sample characters in English, Spanish, French, and Chinese Simplified alphabets:

const MAX_ERR_PX = 1;

describe("Core/measureText", () => {
  let canvas;
  let ctx;

  beforeEach(() => {
    canvas = document.createElement("canvas");
    canvas.width = canvas.height = 1;
    ctx = canvas.getContext("2d", { willReadFrequently: true });
    document.body.appendChild(canvas);
  });

  afterEach(() => {
    document.body.removeChild(canvas);
  });

  it("ascii lowercase", () => {
    ctx.font = "12px sans-serif";
    for (const char of "abcdefghijklmnopqrstuvwxyz0123456789".split("")) {
      const m1 = measureText1(ctx, char, ctx.font, false, true, 1);
      const m2 = measureText2(ctx, char, ctx.font, false, true, 2);
      expect(Math.abs(m1.width - m2.width)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.height - m2.height)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.ascent - m2.ascent)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.descent - m2.descent)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.minx - m2.minx)).toBeLessThanOrEqual(MAX_ERR_PX);
    }
  });

  it("ascii uppercase", () => {
    ctx.font = "12px sans-serif";
    for (const char of "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("")) {
      const m1 = measureText1(ctx, char, ctx.font, false, true, 1);
      const m2 = measureText2(ctx, char, ctx.font, false, true, 2);
      expect(Math.abs(m1.width - m2.width)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.height - m2.height)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.ascent - m2.ascent)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.descent - m2.descent)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.minx - m2.minx)).toBeLessThanOrEqual(MAX_ERR_PX);
    }
  });

  it("chinese simplified", () => {
    ctx.font = "12px sans-serif";
    for (const char of "世界您好".split("")) {
      const m1 = measureText1(ctx, char, ctx.font, false, true, 1);
      const m2 = measureText2(ctx, char, ctx.font, false, true, 2);
      expect(Math.abs(m1.width - m2.width)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.height - m2.height)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.ascent - m2.ascent)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.descent - m2.descent)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.minx - m2.minx)).toBeLessThanOrEqual(MAX_ERR_PX);
    }
  });

  it("spanish", () => {
    ctx.font = "12px sans-serif";
    for (const char of "áéíóúüñ¿¡".split("")) {
      const m1 = measureText1(ctx, char, ctx.font, false, true, 1);
      const m2 = measureText2(ctx, char, ctx.font, false, true, 2);
      expect(Math.abs(m1.width - m2.width)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.height - m2.height)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.ascent - m2.ascent)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.descent - m2.descent)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.minx - m2.minx)).toBeLessThanOrEqual(MAX_ERR_PX);
    }
  });

  it("french", () => {
    ctx.font = "12px sans-serif";
    for (const char of "çéâêîôûàèìòùëïü".split("")) {
      const m1 = measureText1(ctx, char, ctx.font, false, true, 1);
      const m2 = measureText2(ctx, char, ctx.font, false, true, 2);
      expect(Math.abs(m1.width - m2.width)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.height - m2.height)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.ascent - m2.ascent)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.descent - m2.descent)).toBeLessThanOrEqual(MAX_ERR_PX);
      expect(Math.abs(m1.minx - m2.minx)).toBeLessThanOrEqual(MAX_ERR_PX);
    }
  });
});

The purpose of the test is to confirm that no metric is off by more than 1px, for any character, compared to the previous implementation. The tests pass. Notably, if the Math.round calls are removed from the implementation, many of these tests begin to fail.

I also did some basic screenshot comparisons. Visual results are not identical, but — per the 1px target — are very close.

Labels

before after
labels_before labels_after

Difference of a few pixels. Main concern here would be if the font baseline is misaligned, but both appear fine in tests so far.

Sandbox from #11747

before after
punctuation_prod punctuation_round

Some visible differences. Unscientifically, I think writeTextToCanvas is now more tightly fitting the canvas to the text when padding=0, which seems ... not bad? Based on #10649, the previous behavior might not be ideal, but I've tried to just minimize changes and avoid a regression in this PR so far, leaving a fix for future work.

Sandbox from #11705

case image
before padding_prod
after padding_round

Improvement. The padding in the original screenshot was a bug, believed to be caused by our custom implementation not supporting some fonts correctly.

Author checklist

  • I have submitted a Contributor License Agreement
  • I have added my name to CONTRIBUTORS.md
  • I have updated CHANGES.md with a short summary of my change
  • I have added or updated unit tests to ensure consistent code coverage
  • I have updated the inline documentation, and included code examples where relevant
  • I have performed a self-review of my code

PR Dependency Tree

This tree was auto-generated by Charcoal

@github-actions
Copy link

github-actions bot commented Dec 4, 2025

Thank you for the pull request, @donmccurdy!

✅ We can confirm we have a CLA on file for you.

@donmccurdy donmccurdy force-pushed the refactor/remove-custom-measureText branch from bcdfc8d to 558a4e3 Compare December 4, 2025 19:33
@donmccurdy donmccurdy marked this pull request as ready for review December 4, 2025 19:36
@donmccurdy
Copy link
Member Author

@javagl would you mind reviewing this one? 🙏

@javagl
Copy link
Contributor

javagl commented Dec 4, 2025

First getting rid of the custom implementation (and ensuring that this does not change anything else) sounds like a plan. Hopefully, the related issues (e.g. hyphen/underscore handling) can then still be resolved based on that state.

I'll need a short refresher for the last experiments that I did there - that back and forth of actualBoundingBoxDescent vs. the font bounding box (#11747 (comment)). And out of curiosity (and because I tore quite a few hairs out for that other PR), I'll probably have a look at whether the change here also causes the ~"sliiightly more jagged baseline for the veeeery small fonts" (that was one of the things that contributed to that PR not being merged back then). But maybe that was effectively caused by the missing rounds. I'l try to do the review tomorrow (but not later than during the weekend).

@javagl
Copy link
Contributor

javagl commented Dec 5, 2025

That Cesium MeasureText folder had literally been lingering on my Desktop for nearly two years now ("Maybe, one day, I'll have another look at this... 🤔").

Now, it has a revival subfolder.

I started some review/test, but may have to wrap it up later.


First, regarding the test that you posted: I wanted to try this out, and added the following as a spec file

measureTextTempSpec.js

(Just copying both measureText versions on top of what you posted).

Nearly all the tests are failing with

Expected 2 to be less than or equal 1.

Before investigating it further: Is this how this (temporary, local) test is supposed to be run?
If so, a wild guess: Could that failure be due to some ~"resolution scaling" or other browser/OS/hardware(!)-specific setting?

(I said that nearly all the tests are failing. The 'spanish' ones are passing. I don't expect anyone to make this make sense...)


I ran a test similar to the one from #11747 (comment) , using this sandcastle

const viewer = new Cesium.Viewer("cesiumContainer");
viewer._cesiumWidget._creditContainer.style.display = "none";

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222),
  label: {
    text: "Philadelphia",
    font: "12px Helvetica",
    fillColor: Cesium.Color.SKYBLUE,
    outlineColor: Cesium.Color.BLACK,
    outlineWidth: 2,
    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  },
});

And I wanted to post a GIF showing the differences. But there are none (at least, none that are visible even when looking closely)


I tried to check the effect of this PR on #12443 , but the issue seems to be Linux-specific to begin with (I tried it on FireFox on Windows, and couldn't see these inflated labels here. Maybe @anne-gropler wants to try out this branch with the sandcastle from that PR?)


I re-ran the Sandcastle from #11747 (with minor adjustments) - I'll post it as code here, the relevant points are summarized below,

var viewer = new Cesium.Viewer("cesiumContainer");
viewer._cesiumWidget._creditContainer.style.display = "none";

const e = viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, 0, 100),
  billboard: {
    image: Cesium.writeTextToCanvas(
      "                top--                           ",
      { backgroundColor: Cesium.Color.RED }
    ),
    scale: 2,
  },
});

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00003, 100),
  billboard: {
    image: Cesium.writeTextToCanvas("--'----", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00006, 100),
  billboard: {
    image: Cesium.writeTextToCanvas("- ___- - - - '", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00009, 100),
  billboard: {
    image: Cesium.writeTextToCanvas("__--__--__--", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});

// This crashes on `main` with
// Error loading image for billboard: DeveloperError: Expected image.height to be greater than or equal to 1, actual value was 0
// but works now due to the 
// height: Math.max(Math.round(height), 1),
/*/
viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00012, 100),
  billboard: {
    image: Cesium.writeTextToCanvas("_____", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});
//*/

// This crashes on `main` (see above)
/*/
viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00015, 100),
  billboard: {
    image: Cesium.writeTextToCanvas("------", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});
//*/

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00018, 100),
  billboard: {
    image: Cesium.writeTextToCanvas("Bottom Text", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00021, 100),
  billboard: {
    image: Cesium.writeTextToCanvas("Right-to-left: Master (אדון): Hello תלמיד (student): שלום", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00024, 100),
  billboard: {
    image: Cesium.writeTextToCanvas("Below this is a space only", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});

// This (a single space) crashes on `main`
// AND on this branch. Consider changing
// isSpace: ... height: 0 
// to
// isSpace: ... height: 1 
// to avoid that
/*/
viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00027, 100),
  billboard: {
    image: Cesium.writeTextToCanvas(" ", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});
//*/

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00030, 100),
  billboard: {
    image: Cesium.writeTextToCanvas("Above this is a space only", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});


viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00034, 100),
  billboard: {
    image: Cesium.writeTextToCanvas("꧁Javanese rerenggans꧂", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(0, -0.00038, 100),
  billboard: {
    image: Cesium.writeTextToCanvas(" À̴̴̴̴̴̖̖̖̖̖̀̀̀̀ n example for unicode combined characters", {
      backgroundColor: Cesium.Color.RED,
    }),
    scale: 2,
  },
});

viewer.flyTo(viewer.entities, {
  duration: 0
});

Relevant points:

The cases of "_____" and "------" are currently crashing on main with

Error loading image for billboard: DeveloperError: Expected image.height to be greater than or equal to 1, actual value was 0

Both have ~"height of 0", apparently.

They are working with this PR, due to that
height: Math.max(Math.round(height), 1),


The case of " " (a single space) is crashing on main and on this PR, due to the special handling of the isSpace. Changing that special handling to...

  if (isSpace) {
    return {
      width: metrics.width,
      height: 1, // ------------------------ ... return 1 here...
      ascent: 0,
      descent: 0,
      minx: 0,
    };
  }

... could prevent that crash.

One could argue about what should be rendered there. (Nothing? A line? A rectangle?...). But I think that this is something that should at least be anticipated. In doubt, this could be some user input, and the application should not have to manually check this case to prevent a crash. Using height: 1 seems to be a reasonable way to prevent that crash internally.


And... the ꧁Javanese rerenggans꧂are still working.
I think we can all agree that this is the most important point here! 🙂


Again: I'll do the "final" review a bit later. I already looked at the code, and there doesn't seem to be anything controversial.

The main "issue" fow now is about that temporary/local Core/measureText spec. I'd at least like to know how to run it, and why it's not passing for me.

@donmccurdy
Copy link
Member Author

Thanks @javagl!

Nearly all the tests are failing with Expected 2 to be less than or equal 1. Before investigating it further: Is this how this (temporary, local) test is supposed to be run? If so, a wild guess: Could that failure be due to some ~"resolution scaling" or other browser/OS/hardware(!)-specific setting?

Interesting! I'll re-run on my machine just in case I've accidentally changed something, but it may indeed be a browser/OS/hardware difference. I'd originally run the tests in Chrome, on macOS, connected to an external display with resolution such that window.devicePixelRatio = 2.

I'll try some other browsers and display settings to see what happens. If failures persist, perhaps we'll want to add a few more tests to confirm these are differences of "a pixel or two" and not something that will add up to larger problems on multi-character labels.

@javagl
Copy link
Contributor

javagl commented Dec 5, 2025

It's not perfectly clear how thoroughly things should be tested, and how deeply ~"unexpected behaviors" should be investigated. Most of the following is just a deep dive into the craziness of font rendering.


Logging my devicePixelRatio prints 1. I had a short look at whether that could be overridden for some canvas that is only created for the specs, but ... maybe it doesn't matter after all.


I logged some of the outputs and checked the values, to see why the tests are failing. And I found this:

For  f
{width: 3.333984375, height: 8, ascent: 9, descent: -1, minx: -1}
{width: 3.333984375, height: 9, ascent: 9, descent: 0, minx: 1}

Sooo... the difference between the minx values is too large.

This was the case for f (might be ligatures?), and for j (yeah, probably ligatures), and for t (wait ...what?), and for w, x, and z (!?)

For the Chinese characters, all tested ones pass, except for , which also fails due to that minx.

So I commented out all the minx checks, and then, all tests passed...
...
...
... except for Q, which prints

For  Q
measureTextTempSpec.js:171 {width: 9.333984375, height: 8, ascent: 9, descent: -1, minx: 0}
measureTextTempSpec.js:172 {width: 9.333984375, height: 10, ascent: 9, descent: 1, minx: 0}

where the descent and the height are failing.

Easy to fix:
if (labelText.includes("Q")) throw new DeveloperError("Tried to use Q in a label"); 🤪

However, a comparsion of main and this branch with a label aaaaQaaaa looks like this:

CesiumJS MeasureText Revived

where the better one (which doesn't cut off the bottom row) is from this PR, so I'd consider this as a plus.


So there isn't any reason to not approve this, except for that isSpace handling.

Do you think that ensuring a height: 1 for the isSpace case makes sense, at least, to prevent the crash?

@javagl
Copy link
Contributor

javagl commented Dec 6, 2025

confirm these are differences of "a pixel or two" and not something that will add up to larger problems on multi-character labels.

This does not only apply to "multi-character labels", but also to the "small" test cases.

The tests until now had been restricted to "12px sans-serif". I added the following "test case"...

it("all of the above", () => {
    const families = ["sans-serif", "serif", "monospace"];
    const sizes = [6, 9, 12, 16, 24, 96];
    console.log(
      `family;size;char;m1.width;m1.height;m1.ascent;m1.descent;m1.minx;m2.width;m2.height;m2.ascent;m2.descent;m2.minx;absDiff(width);absDiff(height);absDiff(ascent);absDiff(descent);absDiff(minx)`,
    );
    for (const f of families) {
      for (const s of sizes) {
        ctx.font = `${s}px ${f}`;
        for (const char of "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ世界您好áéíóúüñ¿¡çéâêîôûàèìòùëïüß".split(
          "",
        )) {
          const m1 = measureText1(ctx, char, ctx.font, false, true, 1);
          const m2 = measureText2(ctx, char, ctx.font, false, true, 2);

          const d_width = Math.abs(m1.width - m2.width);
          const d_height = Math.abs(m1.height - m2.height);
          const d_ascent = Math.abs(m1.ascent - m2.ascent);
          const d_descent = Math.abs(m1.descent - m2.descent);
          const d_minx = Math.abs(m1.minx - m2.minx);

          console.log(
            `${f};${s}px;${char};${m1.width};${m1.height};${m1.ascent};${m1.descent};${m1.minx};${m2.width};${m2.height};${m2.ascent};${m2.descent};${m2.minx};${d_width};${d_height};${d_ascent};${d_descent};${d_minx}`,
          );
          //expect(Math.abs(m1.width - m2.width)).toBeLessThanOrEqual(MAX_ERR_PX);
          //expect(Math.abs(m1.height - m2.height)).toBeLessThanOrEqual(MAX_ERR_PX);
          //expect(Math.abs(m1.ascent - m2.ascent)).toBeLessThanOrEqual(MAX_ERR_PX);
          //expect(Math.abs(m1.descent - m2.descent)).toBeLessThanOrEqual(MAX_ERR_PX);
          //expect(Math.abs(m1.minx - m2.minx)).toBeLessThanOrEqual(MAX_ERR_PX);
        }
      }
    }
  });

Yeah, it's not really a "test case". It just dumps some data to the console. This data is attached here:

stats2 2025-12-06.zip

The differences increase with the font size. That may be expected. For example, for sans-serif, 24px, char f, the difference of the minx is 6. But for serif, 96px, char , the difference in height is 58(!). This looks like something that could have ~"undersired effects". Comparing main to this branch with

const viewer = new Cesium.Viewer("cesiumContainer");
viewer._cesiumWidget._creditContainer.style.display = "none";

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222),
  label: {
    text: "您",
    font: "96px serif",
    fillColor: Cesium.Color.SKYBLUE,
    outlineColor: Cesium.Color.BLACK,
    outlineWidth: 2,
    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
    showBackground: true,
    backgroundColor: Cesium.Color.RED,
  },
});

does not show a significant difference. I have not yet investigated why that is - maybe none of the callers of that measureText function actually used the original height (which seems to be waaay off here). Maybe they all already twiddled with ascent and descent (maybe even already as a "workaround" to cope with the "wrong heights" - who knows...).

Another "dimension of things that may have to be tested" is the handling of outlineWidth and style. These parameters are no longer passed to the function, which looks like it could be a big deal.

But... the room for tests here is nearly infinite.

For me, the isSpace question is still the only one that prevents this from being merged.

@donmccurdy
Copy link
Member Author

The case of " " (a single space) is crashing on main and on this PR ... Do you think that ensuring a height: 1 for the isSpace case makes sense, at least, to prevent the crash? ... For me, the isSpace question is still the only one that prevents this from being merged.

Currently writeTextToCanvas has these special cases...

//>>includeStart('debug', pragmas.debug);
if (!defined(text)) {
  throw new DeveloperError("text is required.");
}
//>>includeEnd('debug');
if (text === "") {
  return undefined;
}

... and then LabelCollection.js does some special-casing of whitespace-only labels, but not before 'drawing' the whitespace to the canvas here.

My feeling is that the current hard-coded dimensions for whitespace labels in measureText might be a low-level workaround for what would better be a higher-level behavior decision in writeTextToCanvas or LabelCollection. Or arguably writeTextToCanvas could be an internal function, but that's a breaking change.

I'm inclined leave the whitespace handling as it is, for now. Mostly out of fear of exchanging the current error for silently breaking layout on more legitimate labels that just happen to contain whitespace.

Logging my devicePixelRatio prints 1. I had a short look at whether that could be overridden for some canvas that is only created for the specs, but ... maybe it doesn't matter after all.

I don't think this can be changed by JavaScript, but when creating a custom device emulation profile you can specify the devicePixelRatio:

Screenshot 2025-12-09 at 1 50 03 PM

Likely this would change the test results, but IMHO not in a way that is relevant to the behavior under test.

@donmccurdy
Copy link
Member Author

donmccurdy commented Dec 9, 2025

The cases of "_____" and "------" are currently crashing on main ... they are working with this PR...

Does this link crash on main for you? It's working for me. Tested Chrome, Safari, and Firefox on macOS. Interesting that this would be different, but it sounds like it's OK after the PR in any case.

@donmccurdy
Copy link
Member Author

While it does contribute to passing the proposed unit test, I'm worried that minx: Math.round(Math.abs(metrics.actualBoundingBoxLeft)) with Math.abs cannot really be correct.

The custom implementation was unable to detect minx values outside the (hard-coded) padding, and perhaps wasn't quite measuring the same thing, I think. So there's a subtle change here. I am tempted to remove the abs() call, and negate the result to match what I think would be the intended 'minx' meaning. I'll try that and see what happens for unit tests on Chrome, Safari, and Firefox. The character "j" seems to be a useful one to test with the minx property.

  • TODO: Test without using Math.abs to compute minx

@javagl
Copy link
Contributor

javagl commented Dec 9, 2025

Mostly out of fear of exchanging the current error for silently breaking layout on more legitimate labels that just happen to contain whitespace.

It's hard to imagine how it could break the layout, but not knowing all details of that glyph-based atlassing, it's hard to be sure. (I just saw that there are (surprisingly many) whitespace characters, and one could go the extra mile an try to run ~"tests with all of them", but... I think that it would still be fair to categorize them into 'space' and 'all the others'...)

Does [this link] crash on main for you?

Yes! Both Chrome and Firefox (on Windows (with a GeForce 2080 (with a device pixel ratio of 1 (on a Tuesday)))) are crashing with

Error loading image for billboard: DeveloperError: Expected image.height to be greater than or equal to 1, actual value was 0
Error
    at new DeveloperError (https://sandcastle.cesium.com/js/engine/index.js:7458:11)
    at Check.typeOf.number.greaterThanOrEquals (https://sandcastle.cesium.com/js/engine/index.js:7545:11)
    at TexturePacker.pack (https://sandcastle.cesium.com/js/engine/index.js:41348:31)
    at TextureAtlas._resize (https://sandcastle.cesium.com/js/engine/index.js:41687:42)
    at TextureAtlas._processImageQueue (https://sandcastle.cesium.com/js/engine/index.js:41771:14)
    at TextureAtlas.update (https://sandcastle.cesium.com/js/engine/index.js:41818:15)
    at Array.<anonymous> (https://sandcastle.cesium.com/js/engine/index.js:125377:25)
    at callAfterRenderFunctions (https://sandcastle.cesium.com/js/engine/index.js:247583:48)
    at Scene4.render (https://sandcastle.cesium.com/js/engine/index.js:247931:3)
    at CesiumWidget.render (https://sandcastle.cesium.com/js/engine/index.js:251321:17)

This is somehow not unexpected, with the remaining mystery of where a non-0-value is coming from on your system. A wild guess: These lines/characters have a ~"height of 0.8", and with some device pixel ratio based scaling of 2.0 this becomes int(1.6)=1 in one case, and int(0.8)=0 in my case.

The custom implementation was unable to detect minx values outside the (hard-coded) padding

I'd have to re-read the details, but from a quick look, there's some padding of 100, which should be enough...?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

3 participants