Skip to content

Commit

Permalink
images: Split out ImageLoader from images category module
Browse files Browse the repository at this point in the history
  • Loading branch information
ibgreen committed Jul 5, 2020
1 parent 14915b3 commit 03cf854
Show file tree
Hide file tree
Showing 51 changed files with 669 additions and 111 deletions.
7 changes: 7 additions & 0 deletions modules/image/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# @loaders.gl/images

[loaders.gl](https://loaders.gl/docs) is a collection of framework independent 3D and geospatial parsers and encoders.

This module contains loader and writers for images that follow loaders.gl conventions and work under both node and browser.

For documentation please visit the [website](https://loaders.gl).
46 changes: 46 additions & 0 deletions modules/image/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Overview

The `@loaders.gl/image` module contains loader and writers for images that follow loaders.gl conventions and work under both node and browser.

## Installation

```bash
npm install @loaders.gl/image
npm install @loaders.gl/core
```

## API

| Loader | Description |
| -------------------------------------------------------------- | ----------- |
| [`ImageLoader`](modules/image/docs/api-reference/image-loader) | |
| [`ImageWriter`](modules/image/docs/api-reference/image-writer) | |

### Binary Image API

A set of functions that can extract information from "unparsed" binary memory representation of certain image formats. These functions are intended to be called on raw `ArrayBuffer` data, before the `ImageLoader` parses it and converts it to a parsed image type.

These functions are used internally to autodetect if image loader can be used to parse a certain `ArrayBuffer`, but are also available to applications.

| Function | Description |
| ---------------------------------------------------------- | ----------- |
| `getBinaryImageMetadata(imageData : ArrayBuffer) : Object` | |

### Image Type API

A set of functions to help anticipate and control the type of images return by the `ImageLoader`.

| Function | Description |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `isImageTypeSupported(type : string) : boolean` | Check if type is supported by current run-time environment |
| `getDefaultImageType() : string` | Returns the image type selected by default ( `options.image.type: 'auto'` in current run-time environment |

## Image Types

To support image loading on older browsers and Node.js, the `ImageLoader` can return different types, i.e. different representations of the parsed image.

- `ImageBitmap` - An `ImageBitmap` object represents a bitmap image that can be painted to a canvas without undue latency. This is the preferred parsed image representation in the browser. It can also be transferred efficiently between threads. Not available in some older browsers.
- `Image` (aka `HTMLImageElement`) - The traditional HTML image class. Available in all browsers.
- `data` - a memory layout for parsed pixels in node.js. Texture creation functions in headless gl accept `data` images.

See [`ImageLoader`](modules/image/docs/api-reference/image-loader) for more details on options etc.
48 changes: 48 additions & 0 deletions modules/image/docs/api-reference/binary-image-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Binary Image Utilities

Utilities to extract metadata such as image format (MIME type) and size (dimensions) from binary images without parsing the full image. Looks for format-specific headers in the encoded binary data (e.g. encoded JPEG or PNG images).

The format is reported using MIME types strings. Supported binary formats and their MIME types are:

| Format | MIME Type |
| ------ | ------------ |
| PNG | `image/png` |
| JPEG | `image/jpeg` |
| BMP | `image/bmp` |
| GIF | `image/gif` |

## Usage

```js
const response = await fetchFile(imageUrl);
const arrayBuffer = await response.arrayBuffer();

const metadata = getBinaryImageMetadata(arrayBuffer);
if (medata) {
const {width, height, mimeTupe} = metadata;
}
```

## Functions

### getBinaryImageMetadata(imageData: ArrayBuffer | DataView): boolean

Parameters:

- `imageData`: Binary encoded image data.

Returns a metadata object with if the binary data represents a known binary image format.

```js
{
metadata: string;
width: number;
height: number;
}
```

Throws:

- if image is not in a supported binary format.

If `mimeType` is supplied, assumes the image is of that type. If not supplied, first attempts to auto deduce the image format (see `getImageMIMEType`).
25 changes: 25 additions & 0 deletions modules/image/docs/api-reference/parsed-image-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Image Utilities

A small set of image utility functions functions intended to help write image handling code that works across platforms.

Background: The image returned by the [`ImageLoader`](modules/images/docs/api-reference/image-loader.md) depends on the environment, i.e. whether the application is running in a new or old browser, or under Node.js.

## Usage

Determine supported image types in a platform independent way:

```js
import {getDefaultImageType, isImageTypeSupported} from `@loaders.gl/images`;
```

## Functions

### getDefaultImageType(): string

Returns `true` if `type` is one of the types that `@loaders.gl/images` can use on the current platform (depends on browser, or whether running under Node.js).

### isImageTypeSupported(type : string) : boolean

- `type`: value to test

Returns `true` if `type` is one of the types that `@loaders.gl/images` can use on the current platform (depends on browser, or whether running under Node.js).
38 changes: 38 additions & 0 deletions modules/image/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@loaders.gl/image",
"version": "2.3.0-alpha.0",
"description": "Framework-independent loaders and writers for images (PNG, JPG, ...)",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/visgl/loaders.gl"
},
"keywords": [
"webgl",
"loader",
"3d",
"mesh",
"point cloud",
"PLY"
],
"types": "src/index.d.ts",
"main": "dist/es5/index.js",
"module": "dist/esm/index.js",
"esnext": "dist/es6/index.js",
"sideEffects": false,
"files": [
"src",
"dist",
"README.md"
],
"scripts": {
"pre-build": "npm run build-bundle && npm run build-bundle -- --env.dev",
"build-bundle": "webpack --display=minimal --config ../../scripts/bundle.config.js"
},
"dependencies": {
"@loaders.gl/loader-utils": "^2.1.3"
}
}
7 changes: 7 additions & 0 deletions modules/image/src/bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* global window, global */
const moduleExports = require('./index');
const _global = typeof window === 'undefined' ? global : window;
// @ts-ignore
_global.loaders = _global.loaders || {};
// @ts-ignore
module.exports = Object.assign(_global.loaders, moduleExports);
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import parseImage from './lib/parsers/parse-image';
import {getBinaryImageMetadata} from './lib/category-api/binary-image-api';
import {getBinaryImageMetadata} from './lib/api/binary-image-api';
/** @typedef {import('@loaders.gl/loader-utils').LoaderObject} LoaderObject */

// __VERSION__ is injected by babel-plugin-version-inline
Expand All @@ -26,7 +26,7 @@ const ImageLoader = {
mimeTypes: MIME_TYPES,
extensions: EXTENSIONS,
parse: parseImage,
// TODO: byteOffset, byteLength;
// TODOL: Handle (byteOffset, byteLength);
test: arrayBuffer => Boolean(getBinaryImageMetadata(new DataView(arrayBuffer))),
options: {
image: {
Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions modules/image/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export {default as ImageLoader} from './image-loader';
export {default as ImageWriter} from './image-writer';

// IMAGE CATEGORY API

// Image Type API
export {isImageTypeSupported, getDefaultImageType} from './lib/api/image-type';

// Binary Image API
export {getBinaryImageMetadata} from './lib/api/binary-image-api';

// HACK - for texture loaders in image category
export {default as _parseImage} from './lib/parsers/parse-image';
13 changes: 13 additions & 0 deletions modules/image/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export {default as ImageLoader} from './image-loader';
export {default as ImageWriter} from './image-writer';

// IMAGE CATEGORY API

// Image Type API
export {isImageTypeSupported, getDefaultImageType} from './lib/api/image-type';

// Binary Image API
export {getBinaryImageMetadata} from './lib/api/binary-image-api';

// HACK - for texture loaders in image category
export {default as _parseImage} from './lib/parsers/parse-image';
16 changes: 16 additions & 0 deletions modules/image/src/lib/api/image-type.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Image type string used to control or determine the type of images returned from ImageLoader
*/
export type ImageType = 'imagebitmap' | 'image' | 'data';

/**
* Checks if a loaders.gl image type is supported
* @param type image type string
*/
export function isImageTypeSupported(type: ImageType): boolean;

/**
* Returns the "most performant" supported image type on this platform
* @returns image type string
*/
export function getDefaultImageType(): ImageType;
45 changes: 45 additions & 0 deletions modules/image/src/lib/api/image-type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* global ImageBitmap, Image */
import {global, isBrowser} from '@loaders.gl/loader-utils';

// @ts-ignore TS2339: Property does not exist on type
const {_parseImageNode} = global;

const IMAGE_SUPPORTED = typeof Image !== 'undefined'; // NOTE: "false" positives if jsdom is installed
const IMAGE_BITMAP_SUPPORTED = typeof ImageBitmap !== 'undefined';
const NODE_IMAGE_SUPPORTED = Boolean(_parseImageNode);
const DATA_SUPPORTED = isBrowser ? true : NODE_IMAGE_SUPPORTED;

// Checks if a loaders.gl image type is supported
export function isImageTypeSupported(type) {
switch (type) {
case 'auto':
// Should only ever be false in Node.js, if polyfills have not been installed...
return IMAGE_BITMAP_SUPPORTED || IMAGE_SUPPORTED || DATA_SUPPORTED;

case 'imagebitmap':
return IMAGE_BITMAP_SUPPORTED;
case 'image':
return IMAGE_SUPPORTED;
case 'data':
return DATA_SUPPORTED;

default:
throw new Error(`@loaders.gl/images: ${type} not supported`);
}
}

// Returns the best loaders.gl image type supported on current run-time environment
export function getDefaultImageType() {
if (IMAGE_BITMAP_SUPPORTED) {
return 'imagebitmap';
}
if (IMAGE_SUPPORTED) {
return 'image';
}
if (DATA_SUPPORTED) {
return 'data';
}

// This should only happen in Node.js
throw new Error(`Install '@loaders.gl/polyfills' to parse images under Node.js`);
}
37 changes: 37 additions & 0 deletions modules/image/src/lib/deprecated/get-image-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* global Image, ImageBitmap */
import {assert} from '@loaders.gl/loader-utils';

export function getImageData(image) {
switch (getImageTypeOrNull(image)) {
case 'data':
return image;

case 'image':
case 'imagebitmap':
// Extract the image data from the image via a canvas
/* global document */
const canvas = document.createElement('canvas');
// TODO - reuse the canvas?
const context = canvas.getContext('2d');
canvas.width = image.width;
canvas.height = image.height;
context.drawImage(image, 0, 0);
return context.getImageData(0, 0, image.width, image.height);
default:
return assert(false);
}
}

// eslint-disable-next-line complexity
function getImageTypeOrNull(image) {
if (typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap) {
return 'imagebitmap';
}
if (typeof Image !== 'undefined' && image instanceof Image) {
return 'image';
}
if (image && typeof image === 'object' && image.data && image.width && image.height) {
return 'data';
}
return null;
}
45 changes: 45 additions & 0 deletions modules/image/src/lib/deprecated/image-type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* global ImageBitmap, Image */
import {global, isBrowser} from '@loaders.gl/loader-utils';

// @ts-ignore TS2339: Property does not exist on type
const {_parseImageNode} = global;

const IMAGE_SUPPORTED = typeof Image !== 'undefined'; // NOTE: "false" positives if jsdom is installed
const IMAGE_BITMAP_SUPPORTED = typeof ImageBitmap !== 'undefined';
const NODE_IMAGE_SUPPORTED = Boolean(_parseImageNode);
const DATA_SUPPORTED = isBrowser ? true : NODE_IMAGE_SUPPORTED;

// Checks if a loaders.gl image type is supported
export function isImageTypeSupported(type) {
switch (type) {
case 'auto':
// Should only ever be false in Node.js, if polyfills have not been installed...
return IMAGE_BITMAP_SUPPORTED || IMAGE_SUPPORTED || DATA_SUPPORTED;

case 'imagebitmap':
return IMAGE_BITMAP_SUPPORTED;
case 'image':
return IMAGE_SUPPORTED;
case 'data':
return DATA_SUPPORTED;

default:
throw new Error(`@loaders.gl/images: ${type} not supported`);
}
}

// Returns the best loaders.gl image type supported on current run-time environment
export function getDefaultImageType() {
if (IMAGE_BITMAP_SUPPORTED) {
return 'imagebitmap';
}
if (IMAGE_SUPPORTED) {
return 'image';
}
if (DATA_SUPPORTED) {
return 'data';
}

// This should only happen in Node.js
throw new Error(`Install '@loaders.gl/polyfills' to parse images under Node.js`);
}
21 changes: 21 additions & 0 deletions modules/image/src/lib/encoders/encode-image.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

/**
* Returns data bytes representing a compressed image in PNG or JPG format,
* This data can be saved using file system (f) methods or used in a request.
* @param image - Image or Canvas
* @param mimeType
* TODO clean up
* param {String} opt.type='png' - png, jpg or image/png, image/jpg are valid
* param {String} opt.dataURI= - Whether to include a data URI header
export function encodeImage(image: any, mimeType: string): ArrayBuffer;
*/

/**
* Returns data bytes representing a compressed image in PNG or JPG format,
* This data can be saved using file system (f) methods or used in a request.
* @param image - ImageBitmap Image or Canvas
* @param options
* param opt.type='png' - png, jpg or image/png, image/jpg are valid
* param mimeType= - Whether to include a data URI header
*/
export function encodeImage(image: any, type?: string): string;
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Image loading/saving for browser
/* global document, HTMLCanvasElement, Image */

import assert from '../utils/assert';
import {global} from '../utils/globals';
import {assert, global} from '@loaders.gl/loader-utils';

// @ts-ignore TS2339: Property does not exist on type
const {_encodeImageNode} = global;
Expand Down

0 comments on commit 03cf854

Please sign in to comment.