react-native-livechart 3.5.0 → 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"MarkerOverlay.d.ts","sourceRoot":"","sources":["../../src/components/MarkerOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAejD,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,MAAM,EACN,YAAY,EACb,MAAM,UAAU,CAAC;AA8FlB;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,MAAM,EACN,OAAO,EACP,OAAO,EACP,IAAI,EACJ,MAAM,EACN,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,MAAM,CAAC,EAAE,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC;IACrC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,WAAW,CAAC,cAAc,EAAE,CAAC,CAAC;CAC1C,2CAyHA"}
1
+ {"version":3,"file":"MarkerOverlay.d.ts","sourceRoot":"","sources":["../../src/components/MarkerOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAejD,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,MAAM,EACN,YAAY,EACb,MAAM,UAAU,CAAC;AA8FlB;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,MAAM,EACN,OAAO,EACP,OAAO,EACP,IAAI,EACJ,MAAM,EACN,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,MAAM,CAAC,EAAE,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC;IACrC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,WAAW,CAAC,cAAc,EAAE,CAAC,CAAC;CAC1C,2CAgIA"}
@@ -22,7 +22,9 @@ export declare function isConnectorMarker(m: Marker): boolean;
22
22
  export declare function markerAppearanceSig(m: Marker): string;
23
23
  /** One packed glyph in the atlas image: its source rect + box size. */
24
24
  export interface AtlasCell {
25
+ /** Source rect into the (hi-res) atlas texture, in **device** pixels. */
25
26
  rect: SkRect;
27
+ /** Logical (DPR-independent) cell width/height, used to center the blit. */
26
28
  w: number;
27
29
  h: number;
28
30
  }
@@ -31,6 +33,13 @@ export interface MarkerAtlas {
31
33
  image: SkImage | null;
32
34
  /** appearance-signature → cell. */
33
35
  cells: Record<string, AtlasCell>;
36
+ /**
37
+ * Device-pixel scale the texture was rasterized at. Cell `rect`s are in
38
+ * texture (device) pixels, while `w`/`h` stay in logical pixels — so the
39
+ * per-frame blit must apply an `RSXform` scale of `1 / scale` to map the
40
+ * hi-res cell back to its logical on-canvas size. See `buildMarkerAtlas`.
41
+ */
42
+ scale: number;
34
43
  }
35
44
  /**
36
45
  * Rasterize every distinct marker appearance into a single packed atlas image,
@@ -39,6 +48,12 @@ export interface MarkerAtlas {
39
48
  *
40
49
  * Connector kinds (see `isConnectorMarker`) are skipped — they render via a
41
50
  * self-projecting fallback component.
51
+ *
52
+ * The texture is rasterized at `scale` (the screen's device-pixel ratio) so the
53
+ * cells carry enough resolution to stay crisp when blitted onto a retina canvas
54
+ * — every glyph is recorded in logical coords then magnified by `scale`, and the
55
+ * per-frame `drawAtlas` shrinks it back with an `RSXform` scale of `1 / scale`.
56
+ * Without this the logical-sized texture is upscaled ~3× on iOS and looks blurry.
42
57
  */
43
- export declare function buildMarkerAtlas(markers: Marker[], palette: LiveChartPalette, font: SkFont): MarkerAtlas;
58
+ export declare function buildMarkerAtlas(markers: Marker[], palette: LiveChartPalette, font: SkFont, scale?: number): MarkerAtlas;
44
59
  //# sourceMappingURL=markerAtlas.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"markerAtlas.d.ts","sourceRoot":"","sources":["../../src/draw/markerAtlas.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,MAAM,EACX,KAAK,OAAO,EAEZ,KAAK,MAAM,EACZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAErE,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAQpC,iEAAiE;AACjE,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,gBAAgB,GACxB,MAAM,CAaR;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED,uEAAuE;AACvE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,WAAW;IAC1B,8EAA8E;IAC9E,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACtB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAClC;AAuKD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,MAAM,GACX,WAAW,CAsCb"}
1
+ {"version":3,"file":"markerAtlas.d.ts","sourceRoot":"","sources":["../../src/draw/markerAtlas.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,MAAM,EACX,KAAK,OAAO,EAEZ,KAAK,MAAM,EACZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAErE,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAQpC,iEAAiE;AACjE,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,gBAAgB,GACxB,MAAM,CAaR;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED,uEAAuE;AACvE,MAAM,WAAW,SAAS;IACxB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,WAAW;IAC1B,8EAA8E;IAC9E,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACtB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjC;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAuKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,MAAM,EACZ,KAAK,SAAI,GACR,WAAW,CAgDb"}
package/package.json CHANGED
@@ -68,5 +68,5 @@
68
68
  },
69
69
  "sideEffects": false,
70
70
  "types": "./dist/index.d.ts",
71
- "version": "3.5.0"
71
+ "version": "3.5.1"
72
72
  }
@@ -6,6 +6,7 @@ import {
6
6
  type SkFont,
7
7
  } from "@shopify/react-native-skia";
8
8
  import { useMemo, useRef, useState } from "react";
9
+ import { PixelRatio } from "react-native";
9
10
  import {
10
11
  useDerivedValue,
11
12
  useAnimatedReaction,
@@ -189,13 +190,19 @@ export function MarkerOverlay({
189
190
  }, [snapshot]);
190
191
  // Cells bake in resolved colors; include the palette fields they depend on.
191
192
  const paletteKey = `${palette.bgRgb.join(",")}|${palette.line}|${palette.refLine}|${palette.dotUp}|${palette.refLabel}`;
193
+ // Rasterize at the screen's device-pixel ratio so sprites stay crisp on
194
+ // retina canvases instead of being upscaled from a logical-sized texture.
195
+ const dpr = PixelRatio.get();
192
196
  const atlas = useMemo(
193
- () => buildMarkerAtlas(snapshot, palette, font),
197
+ () => buildMarkerAtlas(snapshot, palette, font, dpr),
194
198
  // eslint-disable-next-line react-hooks/exhaustive-deps -- appearanceKey/paletteKey capture the inputs that change cell pixels
195
- [appearanceKey, paletteKey, font],
199
+ [appearanceKey, paletteKey, font, dpr],
196
200
  );
197
201
  const cells: Record<string, AtlasCell> = atlas.cells;
198
202
  const atlasImage = atlas.image;
203
+ // Inverse of the texture's device-pixel scale; shrinks each hi-res cell back
204
+ // to its logical on-canvas size in the per-frame blit.
205
+ const invScale = 1 / atlas.scale;
199
206
 
200
207
  // One pooled, ping-ponged projection buffer reused every frame (no per-frame
201
208
  // array allocation for the projection itself).
@@ -240,15 +247,16 @@ export function MarkerOverlay({
240
247
  if (isConnectorMarker(m)) continue;
241
248
  const cell = cells[markerAppearanceSig(m)];
242
249
  if (!cell) continue;
243
- // Center the cell on the projected point (RSXform: scale 1, no rotation).
250
+ // Center the cell on the projected point. The cell's source rect is in
251
+ // device pixels, so scale by 1/dpr to land it at its logical size.
244
252
  transforms.push(
245
- Skia.RSXform(1, 0, pt.x - cell.w / 2, pt.y - cell.h / 2),
253
+ Skia.RSXform(invScale, 0, pt.x - cell.w / 2, pt.y - cell.h / 2),
246
254
  );
247
255
  sprites.push(cell.rect);
248
256
  }
249
257
  return { transforms, sprites };
250
258
  },
251
- [cells, markers, engine, padding, series, lineData],
259
+ [cells, invScale, markers, engine, padding, series, lineData],
252
260
  );
253
261
  const transforms = useDerivedValue(() => atlasData.get().transforms, [atlasData]);
254
262
  const sprites = useDerivedValue(() => atlasData.get().sprites, [atlasData]);
@@ -74,7 +74,9 @@ export function markerAppearanceSig(m: Marker): string {
74
74
 
75
75
  /** One packed glyph in the atlas image: its source rect + box size. */
76
76
  export interface AtlasCell {
77
+ /** Source rect into the (hi-res) atlas texture, in **device** pixels. */
77
78
  rect: SkRect;
79
+ /** Logical (DPR-independent) cell width/height, used to center the blit. */
78
80
  w: number;
79
81
  h: number;
80
82
  }
@@ -84,9 +86,16 @@ export interface MarkerAtlas {
84
86
  image: SkImage | null;
85
87
  /** appearance-signature → cell. */
86
88
  cells: Record<string, AtlasCell>;
89
+ /**
90
+ * Device-pixel scale the texture was rasterized at. Cell `rect`s are in
91
+ * texture (device) pixels, while `w`/`h` stay in logical pixels — so the
92
+ * per-frame blit must apply an `RSXform` scale of `1 / scale` to map the
93
+ * hi-res cell back to its logical on-canvas size. See `buildMarkerAtlas`.
94
+ */
95
+ scale: number;
87
96
  }
88
97
 
89
- const EMPTY_ATLAS: MarkerAtlas = { image: null, cells: {} };
98
+ const EMPTY_ATLAS: MarkerAtlas = { image: null, cells: {}, scale: 1 };
90
99
 
91
100
  function fillPaint(color: string): SkPaint {
92
101
  const p = Skia.Paint();
@@ -258,11 +267,18 @@ function cellSpec(m: Marker, palette: LiveChartPalette, font: SkFont): CellSpec
258
267
  *
259
268
  * Connector kinds (see `isConnectorMarker`) are skipped — they render via a
260
269
  * self-projecting fallback component.
270
+ *
271
+ * The texture is rasterized at `scale` (the screen's device-pixel ratio) so the
272
+ * cells carry enough resolution to stay crisp when blitted onto a retina canvas
273
+ * — every glyph is recorded in logical coords then magnified by `scale`, and the
274
+ * per-frame `drawAtlas` shrinks it back with an `RSXform` scale of `1 / scale`.
275
+ * Without this the logical-sized texture is upscaled ~3× on iOS and looks blurry.
261
276
  */
262
277
  export function buildMarkerAtlas(
263
278
  markers: Marker[],
264
279
  palette: LiveChartPalette,
265
280
  font: SkFont,
281
+ scale = 1,
266
282
  ): MarkerAtlas {
267
283
  const seen = new Set<string>();
268
284
  const specs: { sig: string; spec: CellSpec }[] = [];
@@ -282,11 +298,16 @@ export function buildMarkerAtlas(
282
298
  totalW += specs[i].spec.w;
283
299
  if (specs[i].spec.h > maxH) maxH = specs[i].spec.h;
284
300
  }
301
+ // Logical packed size; the texture itself is `scale`× larger in each axis.
285
302
  const W = Math.max(1, Math.ceil(totalW));
286
303
  const H = Math.max(1, Math.ceil(maxH));
304
+ const texW = Math.max(1, Math.ceil(W * scale));
305
+ const texH = Math.max(1, Math.ceil(H * scale));
287
306
 
288
307
  const recorder = Skia.PictureRecorder();
289
- const canvas = recorder.beginRecording(Skia.XYWHRect(0, 0, W, H));
308
+ const canvas = recorder.beginRecording(Skia.XYWHRect(0, 0, texW, texH));
309
+ // Magnify so glyphs drawn in logical coords fill the hi-res texture.
310
+ canvas.scale(scale, scale);
290
311
  const cells: Record<string, AtlasCell> = {};
291
312
  let x = 0;
292
313
  for (let i = 0; i < specs.length; i++) {
@@ -295,10 +316,15 @@ export function buildMarkerAtlas(
295
316
  canvas.translate(x, 0);
296
317
  spec.draw(canvas, spec.w / 2, H / 2);
297
318
  canvas.restore();
298
- cells[sig] = { rect: Skia.XYWHRect(x, 0, spec.w, H), w: spec.w, h: H };
319
+ // Source rect indexes the device-pixel texture; w/h stay logical.
320
+ cells[sig] = {
321
+ rect: Skia.XYWHRect(x * scale, 0, spec.w * scale, texH),
322
+ w: spec.w,
323
+ h: H,
324
+ };
299
325
  x += spec.w;
300
326
  }
301
327
  const picture = recorder.finishRecordingAsPicture();
302
- const image = drawAsImageFromPicture(picture, { width: W, height: H });
303
- return { image, cells };
328
+ const image = drawAsImageFromPicture(picture, { width: texW, height: texH });
329
+ return { image, cells, scale };
304
330
  }