gitea源码

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. type PngChunk = {
  2. name: string,
  3. data: Uint8Array,
  4. };
  5. export async function pngChunks(blob: Blob): Promise<PngChunk[]> {
  6. const uint8arr = new Uint8Array(await blob.arrayBuffer());
  7. const chunks: PngChunk[] = [];
  8. if (uint8arr.length < 12) return chunks;
  9. const view = new DataView(uint8arr.buffer);
  10. if (view.getBigUint64(0) !== 9894494448401390090n) return chunks;
  11. const decoder = new TextDecoder();
  12. let index = 8;
  13. while (index < uint8arr.length) {
  14. const len = view.getUint32(index);
  15. chunks.push({
  16. name: decoder.decode(uint8arr.slice(index + 4, index + 8)),
  17. data: uint8arr.slice(index + 8, index + 8 + len),
  18. });
  19. index += len + 12;
  20. }
  21. return chunks;
  22. }
  23. type ImageInfo = {
  24. width?: number,
  25. dppx?: number,
  26. };
  27. /** decode a image and try to obtain width and dppx. It will never throw but instead
  28. * return default values. */
  29. export async function imageInfo(blob: Blob): Promise<ImageInfo> {
  30. let width = 0, dppx = 1; // dppx: 1 dot per pixel for non-HiDPI screens
  31. if (blob.type === 'image/png') { // only png is supported currently
  32. try {
  33. for (const {name, data} of await pngChunks(blob)) {
  34. const view = new DataView(data.buffer);
  35. if (name === 'IHDR' && data?.length) {
  36. // extract width from mandatory IHDR chunk
  37. width = view.getUint32(0);
  38. } else if (name === 'pHYs' && data?.length) {
  39. // extract dppx from optional pHYs chunk, assuming pixels are square
  40. const unit = view.getUint8(8);
  41. if (unit === 1) {
  42. dppx = Math.round(view.getUint32(0) / 39.3701) / 72; // meter to inch to dppx
  43. }
  44. }
  45. }
  46. } catch {}
  47. } else {
  48. return {}; // no image info for non-image files
  49. }
  50. return {width, dppx};
  51. }