bare-media 1.7.0 → 1.8.0
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.
- package/README.md +16 -0
- package/client.js +1 -1
- package/package.json +3 -3
- package/shared/spec/build.js +6 -0
- package/shared/spec/hrpc/hrpc.json +14 -1
- package/shared/spec/hrpc/index.js +15 -3
- package/shared/spec/hrpc/messages.js +108 -2
- package/shared/spec/schema/index.js +108 -2
- package/shared/spec/schema/schema.json +72 -1
- package/shared/spec/schema.js +56 -0
- package/worker/index.js +1 -0
- package/worker/media.js +59 -14
- package/worker/util.js +19 -0
package/README.md
CHANGED
|
@@ -79,6 +79,22 @@ Decode an image to RGBA
|
|
|
79
79
|
| `buffer` | object | Bytes of the input file |
|
|
80
80
|
| `mimetype` | string | Media type of the input file. If not provided it will be detected |
|
|
81
81
|
|
|
82
|
+
### cropImage()
|
|
83
|
+
|
|
84
|
+
Crop an image
|
|
85
|
+
|
|
86
|
+
| Property | Type | Description |
|
|
87
|
+
| ---------- | ------ | ------------------------------------------------------------------------- |
|
|
88
|
+
| `path` | string | Path to the input file. Either `path`, `httpLink` or `buffer` is required |
|
|
89
|
+
| `httpLink` | string | Http link to the input file |
|
|
90
|
+
| `buffer` | object | Bytes of the input file |
|
|
91
|
+
| `mimetype` | string | Media type of the input file. If not provided it will be detected |
|
|
92
|
+
| `left` | number | Offset from left edge |
|
|
93
|
+
| `top` | number | Offset from top edge |
|
|
94
|
+
| `width` | number | Width of the region to crop |
|
|
95
|
+
| `height` | number | Height of the region to crop |
|
|
96
|
+
| `format` | string | Media type for the cropped image. Default same as the input image |
|
|
97
|
+
|
|
82
98
|
## License
|
|
83
99
|
|
|
84
100
|
Apache-2.0
|
package/client.js
CHANGED
|
@@ -24,7 +24,7 @@ export class WorkerClient extends ReadyResource {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
#attachMethods() {
|
|
27
|
-
const methods = ['createPreview', 'decodeImage']
|
|
27
|
+
const methods = ['createPreview', 'decodeImage', 'cropImage']
|
|
28
28
|
|
|
29
29
|
for (const method of methods) {
|
|
30
30
|
this[method] = async (...args) => {
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-media",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build:rpc": "cd shared/spec && bare ./build.js",
|
|
8
8
|
"format": "prettier --write .",
|
|
9
9
|
"format:check": "prettier --check .",
|
|
10
|
-
"lint": "lunte",
|
|
11
|
-
"test": "
|
|
10
|
+
"lint": "npm run format:check && lunte",
|
|
11
|
+
"test": "brittle-bare test/index.js"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [],
|
|
14
14
|
"author": "Holepunch Inc",
|
package/shared/spec/build.js
CHANGED
|
@@ -25,4 +25,10 @@ ns.register({
|
|
|
25
25
|
response: { name: '@media/decode-image-response', stream: false }
|
|
26
26
|
})
|
|
27
27
|
|
|
28
|
+
ns.register({
|
|
29
|
+
name: 'crop-image',
|
|
30
|
+
request: { name: '@media/crop-image-request', stream: false },
|
|
31
|
+
response: { name: '@media/crop-image-response', stream: false }
|
|
32
|
+
})
|
|
33
|
+
|
|
28
34
|
HRPCBuilder.toDisk(builder)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 2,
|
|
3
3
|
"schema": [
|
|
4
4
|
{
|
|
5
5
|
"id": 0,
|
|
@@ -26,6 +26,19 @@
|
|
|
26
26
|
"stream": false
|
|
27
27
|
},
|
|
28
28
|
"version": 1
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": 2,
|
|
32
|
+
"name": "@media/crop-image",
|
|
33
|
+
"request": {
|
|
34
|
+
"name": "@media/crop-image-request",
|
|
35
|
+
"stream": false
|
|
36
|
+
},
|
|
37
|
+
"response": {
|
|
38
|
+
"name": "@media/crop-image-response",
|
|
39
|
+
"stream": false
|
|
40
|
+
},
|
|
41
|
+
"version": 2
|
|
29
42
|
}
|
|
30
43
|
]
|
|
31
44
|
}
|
|
@@ -9,7 +9,9 @@ const methods = new Map([
|
|
|
9
9
|
['@media/create-preview', 0],
|
|
10
10
|
[0, '@media/create-preview'],
|
|
11
11
|
['@media/decode-image', 1],
|
|
12
|
-
[1, '@media/decode-image']
|
|
12
|
+
[1, '@media/decode-image'],
|
|
13
|
+
['@media/crop-image', 2],
|
|
14
|
+
[2, '@media/crop-image']
|
|
13
15
|
])
|
|
14
16
|
|
|
15
17
|
class HRPC {
|
|
@@ -18,11 +20,13 @@ class HRPC {
|
|
|
18
20
|
this._handlers = []
|
|
19
21
|
this._requestEncodings = new Map([
|
|
20
22
|
['@media/create-preview', getEncoding('@media/create-preview-request')],
|
|
21
|
-
['@media/decode-image', getEncoding('@media/decode-image-request')]
|
|
23
|
+
['@media/decode-image', getEncoding('@media/decode-image-request')],
|
|
24
|
+
['@media/crop-image', getEncoding('@media/crop-image-request')]
|
|
22
25
|
])
|
|
23
26
|
this._responseEncodings = new Map([
|
|
24
27
|
['@media/create-preview', getEncoding('@media/create-preview-response')],
|
|
25
|
-
['@media/decode-image', getEncoding('@media/decode-image-response')]
|
|
28
|
+
['@media/decode-image', getEncoding('@media/decode-image-response')],
|
|
29
|
+
['@media/crop-image', getEncoding('@media/crop-image-response')]
|
|
26
30
|
])
|
|
27
31
|
this._rpc = new RPC(stream, async (req) => {
|
|
28
32
|
const command = methods.get(req.command)
|
|
@@ -125,6 +129,10 @@ class HRPC {
|
|
|
125
129
|
return this._call('@media/decode-image', args)
|
|
126
130
|
}
|
|
127
131
|
|
|
132
|
+
async cropImage(args) {
|
|
133
|
+
return this._call('@media/crop-image', args)
|
|
134
|
+
}
|
|
135
|
+
|
|
128
136
|
onCreatePreview(responseFn) {
|
|
129
137
|
this._handlers['@media/create-preview'] = responseFn
|
|
130
138
|
}
|
|
@@ -133,6 +141,10 @@ class HRPC {
|
|
|
133
141
|
this._handlers['@media/decode-image'] = responseFn
|
|
134
142
|
}
|
|
135
143
|
|
|
144
|
+
onCropImage(responseFn) {
|
|
145
|
+
this._handlers['@media/crop-image'] = responseFn
|
|
146
|
+
}
|
|
147
|
+
|
|
136
148
|
_requestIsStream(command) {
|
|
137
149
|
return [].includes(command)
|
|
138
150
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// This file is autogenerated by the hyperschema compiler
|
|
2
|
-
// Schema Version:
|
|
2
|
+
// Schema Version: 2
|
|
3
3
|
/* eslint-disable camelcase */
|
|
4
4
|
/* eslint-disable quotes */
|
|
5
|
+
/* eslint-disable space-before-function-paren */
|
|
5
6
|
|
|
6
7
|
import { c } from 'hyperschema/runtime'
|
|
7
8
|
|
|
8
|
-
const VERSION =
|
|
9
|
+
const VERSION = 2
|
|
9
10
|
|
|
10
11
|
// eslint-disable-next-line no-unused-vars
|
|
11
12
|
let version = VERSION
|
|
@@ -260,6 +261,107 @@ const encoding6 = {
|
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
|
|
264
|
+
// @media/crop-image-request
|
|
265
|
+
const encoding7 = {
|
|
266
|
+
preencode(state, m) {
|
|
267
|
+
const flags =
|
|
268
|
+
(version >= 2 && m.path ? 1 : 0) |
|
|
269
|
+
(version >= 2 && m.httpLink ? 2 : 0) |
|
|
270
|
+
(version >= 2 && m.buffer ? 4 : 0) |
|
|
271
|
+
(version >= 2 && m.mimetype ? 8 : 0) |
|
|
272
|
+
(version >= 2 && m.left ? 16 : 0) |
|
|
273
|
+
(version >= 2 && m.top ? 32 : 0) |
|
|
274
|
+
(version >= 2 && m.width ? 64 : 0) |
|
|
275
|
+
(version >= 2 && m.height ? 128 : 0) |
|
|
276
|
+
(version >= 2 && m.format ? 256 : 0)
|
|
277
|
+
|
|
278
|
+
c.uint.preencode(state, flags)
|
|
279
|
+
|
|
280
|
+
if (version >= 2 && m.path) c.string.preencode(state, m.path)
|
|
281
|
+
if (version >= 2 && m.httpLink) c.string.preencode(state, m.httpLink)
|
|
282
|
+
if (version >= 2 && m.buffer) c.buffer.preencode(state, m.buffer)
|
|
283
|
+
if (version >= 2 && m.mimetype) c.string.preencode(state, m.mimetype)
|
|
284
|
+
if (version >= 2 && m.left) c.uint.preencode(state, m.left)
|
|
285
|
+
if (version >= 2 && m.top) c.uint.preencode(state, m.top)
|
|
286
|
+
if (version >= 2 && m.width) c.uint.preencode(state, m.width)
|
|
287
|
+
if (version >= 2 && m.height) c.uint.preencode(state, m.height)
|
|
288
|
+
if (version >= 2 && m.format) c.string.preencode(state, m.format)
|
|
289
|
+
},
|
|
290
|
+
encode(state, m) {
|
|
291
|
+
const flags =
|
|
292
|
+
(version >= 2 && m.path ? 1 : 0) |
|
|
293
|
+
(version >= 2 && m.httpLink ? 2 : 0) |
|
|
294
|
+
(version >= 2 && m.buffer ? 4 : 0) |
|
|
295
|
+
(version >= 2 && m.mimetype ? 8 : 0) |
|
|
296
|
+
(version >= 2 && m.left ? 16 : 0) |
|
|
297
|
+
(version >= 2 && m.top ? 32 : 0) |
|
|
298
|
+
(version >= 2 && m.width ? 64 : 0) |
|
|
299
|
+
(version >= 2 && m.height ? 128 : 0) |
|
|
300
|
+
(version >= 2 && m.format ? 256 : 0)
|
|
301
|
+
|
|
302
|
+
c.uint.encode(state, flags)
|
|
303
|
+
|
|
304
|
+
if (version >= 2 && m.path) c.string.encode(state, m.path)
|
|
305
|
+
if (version >= 2 && m.httpLink) c.string.encode(state, m.httpLink)
|
|
306
|
+
if (version >= 2 && m.buffer) c.buffer.encode(state, m.buffer)
|
|
307
|
+
if (version >= 2 && m.mimetype) c.string.encode(state, m.mimetype)
|
|
308
|
+
if (version >= 2 && m.left) c.uint.encode(state, m.left)
|
|
309
|
+
if (version >= 2 && m.top) c.uint.encode(state, m.top)
|
|
310
|
+
if (version >= 2 && m.width) c.uint.encode(state, m.width)
|
|
311
|
+
if (version >= 2 && m.height) c.uint.encode(state, m.height)
|
|
312
|
+
if (version >= 2 && m.format) c.string.encode(state, m.format)
|
|
313
|
+
},
|
|
314
|
+
decode(state) {
|
|
315
|
+
const flags = c.uint.decode(state)
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
path: version >= 2 && (flags & 1) !== 0 ? c.string.decode(state) : null,
|
|
319
|
+
httpLink:
|
|
320
|
+
version >= 2 && (flags & 2) !== 0 ? c.string.decode(state) : null,
|
|
321
|
+
buffer: version >= 2 && (flags & 4) !== 0 ? c.buffer.decode(state) : null,
|
|
322
|
+
mimetype:
|
|
323
|
+
version >= 2 && (flags & 8) !== 0 ? c.string.decode(state) : null,
|
|
324
|
+
left: version >= 2 && (flags & 16) !== 0 ? c.uint.decode(state) : 0,
|
|
325
|
+
top: version >= 2 && (flags & 32) !== 0 ? c.uint.decode(state) : 0,
|
|
326
|
+
width: version >= 2 && (flags & 64) !== 0 ? c.uint.decode(state) : 0,
|
|
327
|
+
height: version >= 2 && (flags & 128) !== 0 ? c.uint.decode(state) : 0,
|
|
328
|
+
format:
|
|
329
|
+
version >= 2 && (flags & 256) !== 0 ? c.string.decode(state) : null
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// @media/crop-image-response.metadata
|
|
335
|
+
const encoding8_0 = encoding2_0
|
|
336
|
+
|
|
337
|
+
// @media/crop-image-response
|
|
338
|
+
const encoding8 = {
|
|
339
|
+
preencode(state, m) {
|
|
340
|
+
state.end++ // max flag is 2 so always one byte
|
|
341
|
+
|
|
342
|
+
if (version >= 2 && m.metadata) encoding8_0.preencode(state, m.metadata)
|
|
343
|
+
if (version >= 2 && m.data) c.buffer.preencode(state, m.data)
|
|
344
|
+
},
|
|
345
|
+
encode(state, m) {
|
|
346
|
+
const flags =
|
|
347
|
+
(version >= 2 && m.metadata ? 1 : 0) | (version >= 2 && m.data ? 2 : 0)
|
|
348
|
+
|
|
349
|
+
c.uint.encode(state, flags)
|
|
350
|
+
|
|
351
|
+
if (version >= 2 && m.metadata) encoding8_0.encode(state, m.metadata)
|
|
352
|
+
if (version >= 2 && m.data) c.buffer.encode(state, m.data)
|
|
353
|
+
},
|
|
354
|
+
decode(state) {
|
|
355
|
+
const flags = c.uint.decode(state)
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
metadata:
|
|
359
|
+
version >= 2 && (flags & 1) !== 0 ? encoding8_0.decode(state) : null,
|
|
360
|
+
data: version >= 2 && (flags & 2) !== 0 ? c.buffer.decode(state) : null
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
263
365
|
function setVersion(v) {
|
|
264
366
|
version = v
|
|
265
367
|
}
|
|
@@ -297,6 +399,10 @@ function getEncoding(name) {
|
|
|
297
399
|
return encoding5
|
|
298
400
|
case '@media/decode-image-response':
|
|
299
401
|
return encoding6
|
|
402
|
+
case '@media/crop-image-request':
|
|
403
|
+
return encoding7
|
|
404
|
+
case '@media/crop-image-response':
|
|
405
|
+
return encoding8
|
|
300
406
|
default:
|
|
301
407
|
throw new Error('Encoder not found ' + name)
|
|
302
408
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// This file is autogenerated by the hyperschema compiler
|
|
2
|
-
// Schema Version:
|
|
2
|
+
// Schema Version: 2
|
|
3
3
|
/* eslint-disable camelcase */
|
|
4
4
|
/* eslint-disable quotes */
|
|
5
|
+
/* eslint-disable space-before-function-paren */
|
|
5
6
|
|
|
6
7
|
import { c } from 'hyperschema/runtime'
|
|
7
8
|
|
|
8
|
-
const VERSION =
|
|
9
|
+
const VERSION = 2
|
|
9
10
|
|
|
10
11
|
// eslint-disable-next-line no-unused-vars
|
|
11
12
|
let version = VERSION
|
|
@@ -260,6 +261,107 @@ const encoding6 = {
|
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
|
|
264
|
+
// @media/crop-image-request
|
|
265
|
+
const encoding7 = {
|
|
266
|
+
preencode(state, m) {
|
|
267
|
+
const flags =
|
|
268
|
+
(version >= 2 && m.path ? 1 : 0) |
|
|
269
|
+
(version >= 2 && m.httpLink ? 2 : 0) |
|
|
270
|
+
(version >= 2 && m.buffer ? 4 : 0) |
|
|
271
|
+
(version >= 2 && m.mimetype ? 8 : 0) |
|
|
272
|
+
(version >= 2 && m.left ? 16 : 0) |
|
|
273
|
+
(version >= 2 && m.top ? 32 : 0) |
|
|
274
|
+
(version >= 2 && m.width ? 64 : 0) |
|
|
275
|
+
(version >= 2 && m.height ? 128 : 0) |
|
|
276
|
+
(version >= 2 && m.format ? 256 : 0)
|
|
277
|
+
|
|
278
|
+
c.uint.preencode(state, flags)
|
|
279
|
+
|
|
280
|
+
if (version >= 2 && m.path) c.string.preencode(state, m.path)
|
|
281
|
+
if (version >= 2 && m.httpLink) c.string.preencode(state, m.httpLink)
|
|
282
|
+
if (version >= 2 && m.buffer) c.buffer.preencode(state, m.buffer)
|
|
283
|
+
if (version >= 2 && m.mimetype) c.string.preencode(state, m.mimetype)
|
|
284
|
+
if (version >= 2 && m.left) c.uint.preencode(state, m.left)
|
|
285
|
+
if (version >= 2 && m.top) c.uint.preencode(state, m.top)
|
|
286
|
+
if (version >= 2 && m.width) c.uint.preencode(state, m.width)
|
|
287
|
+
if (version >= 2 && m.height) c.uint.preencode(state, m.height)
|
|
288
|
+
if (version >= 2 && m.format) c.string.preencode(state, m.format)
|
|
289
|
+
},
|
|
290
|
+
encode(state, m) {
|
|
291
|
+
const flags =
|
|
292
|
+
(version >= 2 && m.path ? 1 : 0) |
|
|
293
|
+
(version >= 2 && m.httpLink ? 2 : 0) |
|
|
294
|
+
(version >= 2 && m.buffer ? 4 : 0) |
|
|
295
|
+
(version >= 2 && m.mimetype ? 8 : 0) |
|
|
296
|
+
(version >= 2 && m.left ? 16 : 0) |
|
|
297
|
+
(version >= 2 && m.top ? 32 : 0) |
|
|
298
|
+
(version >= 2 && m.width ? 64 : 0) |
|
|
299
|
+
(version >= 2 && m.height ? 128 : 0) |
|
|
300
|
+
(version >= 2 && m.format ? 256 : 0)
|
|
301
|
+
|
|
302
|
+
c.uint.encode(state, flags)
|
|
303
|
+
|
|
304
|
+
if (version >= 2 && m.path) c.string.encode(state, m.path)
|
|
305
|
+
if (version >= 2 && m.httpLink) c.string.encode(state, m.httpLink)
|
|
306
|
+
if (version >= 2 && m.buffer) c.buffer.encode(state, m.buffer)
|
|
307
|
+
if (version >= 2 && m.mimetype) c.string.encode(state, m.mimetype)
|
|
308
|
+
if (version >= 2 && m.left) c.uint.encode(state, m.left)
|
|
309
|
+
if (version >= 2 && m.top) c.uint.encode(state, m.top)
|
|
310
|
+
if (version >= 2 && m.width) c.uint.encode(state, m.width)
|
|
311
|
+
if (version >= 2 && m.height) c.uint.encode(state, m.height)
|
|
312
|
+
if (version >= 2 && m.format) c.string.encode(state, m.format)
|
|
313
|
+
},
|
|
314
|
+
decode(state) {
|
|
315
|
+
const flags = c.uint.decode(state)
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
path: version >= 2 && (flags & 1) !== 0 ? c.string.decode(state) : null,
|
|
319
|
+
httpLink:
|
|
320
|
+
version >= 2 && (flags & 2) !== 0 ? c.string.decode(state) : null,
|
|
321
|
+
buffer: version >= 2 && (flags & 4) !== 0 ? c.buffer.decode(state) : null,
|
|
322
|
+
mimetype:
|
|
323
|
+
version >= 2 && (flags & 8) !== 0 ? c.string.decode(state) : null,
|
|
324
|
+
left: version >= 2 && (flags & 16) !== 0 ? c.uint.decode(state) : 0,
|
|
325
|
+
top: version >= 2 && (flags & 32) !== 0 ? c.uint.decode(state) : 0,
|
|
326
|
+
width: version >= 2 && (flags & 64) !== 0 ? c.uint.decode(state) : 0,
|
|
327
|
+
height: version >= 2 && (flags & 128) !== 0 ? c.uint.decode(state) : 0,
|
|
328
|
+
format:
|
|
329
|
+
version >= 2 && (flags & 256) !== 0 ? c.string.decode(state) : null
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// @media/crop-image-response.metadata
|
|
335
|
+
const encoding8_0 = encoding2_0
|
|
336
|
+
|
|
337
|
+
// @media/crop-image-response
|
|
338
|
+
const encoding8 = {
|
|
339
|
+
preencode(state, m) {
|
|
340
|
+
state.end++ // max flag is 2 so always one byte
|
|
341
|
+
|
|
342
|
+
if (version >= 2 && m.metadata) encoding8_0.preencode(state, m.metadata)
|
|
343
|
+
if (version >= 2 && m.data) c.buffer.preencode(state, m.data)
|
|
344
|
+
},
|
|
345
|
+
encode(state, m) {
|
|
346
|
+
const flags =
|
|
347
|
+
(version >= 2 && m.metadata ? 1 : 0) | (version >= 2 && m.data ? 2 : 0)
|
|
348
|
+
|
|
349
|
+
c.uint.encode(state, flags)
|
|
350
|
+
|
|
351
|
+
if (version >= 2 && m.metadata) encoding8_0.encode(state, m.metadata)
|
|
352
|
+
if (version >= 2 && m.data) c.buffer.encode(state, m.data)
|
|
353
|
+
},
|
|
354
|
+
decode(state) {
|
|
355
|
+
const flags = c.uint.decode(state)
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
metadata:
|
|
359
|
+
version >= 2 && (flags & 1) !== 0 ? encoding8_0.decode(state) : null,
|
|
360
|
+
data: version >= 2 && (flags & 2) !== 0 ? c.buffer.decode(state) : null
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
263
365
|
function setVersion(v) {
|
|
264
366
|
version = v
|
|
265
367
|
}
|
|
@@ -297,6 +399,10 @@ function getEncoding(name) {
|
|
|
297
399
|
return encoding5
|
|
298
400
|
case '@media/decode-image-response':
|
|
299
401
|
return encoding6
|
|
402
|
+
case '@media/crop-image-request':
|
|
403
|
+
return encoding7
|
|
404
|
+
case '@media/crop-image-response':
|
|
405
|
+
return encoding8
|
|
300
406
|
default:
|
|
301
407
|
throw new Error('Encoder not found ' + name)
|
|
302
408
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 2,
|
|
3
3
|
"schema": [
|
|
4
4
|
{
|
|
5
5
|
"name": "dimensions",
|
|
@@ -190,6 +190,77 @@
|
|
|
190
190
|
"version": 1
|
|
191
191
|
}
|
|
192
192
|
]
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"name": "crop-image-request",
|
|
196
|
+
"namespace": "media",
|
|
197
|
+
"compact": false,
|
|
198
|
+
"flagsPosition": 0,
|
|
199
|
+
"fields": [
|
|
200
|
+
{
|
|
201
|
+
"name": "path",
|
|
202
|
+
"type": "string",
|
|
203
|
+
"version": 2
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"name": "httpLink",
|
|
207
|
+
"type": "string",
|
|
208
|
+
"version": 2
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"name": "buffer",
|
|
212
|
+
"type": "buffer",
|
|
213
|
+
"version": 2
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"name": "mimetype",
|
|
217
|
+
"type": "string",
|
|
218
|
+
"version": 2
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"name": "left",
|
|
222
|
+
"type": "uint",
|
|
223
|
+
"version": 2
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"name": "top",
|
|
227
|
+
"type": "uint",
|
|
228
|
+
"version": 2
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"name": "width",
|
|
232
|
+
"type": "uint",
|
|
233
|
+
"version": 2
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"name": "height",
|
|
237
|
+
"type": "uint",
|
|
238
|
+
"version": 2
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"name": "format",
|
|
242
|
+
"type": "string",
|
|
243
|
+
"version": 2
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"name": "crop-image-response",
|
|
249
|
+
"namespace": "media",
|
|
250
|
+
"compact": false,
|
|
251
|
+
"flagsPosition": 0,
|
|
252
|
+
"fields": [
|
|
253
|
+
{
|
|
254
|
+
"name": "metadata",
|
|
255
|
+
"type": "@media/metadata",
|
|
256
|
+
"version": 2
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"name": "data",
|
|
260
|
+
"type": "buffer",
|
|
261
|
+
"version": 2
|
|
262
|
+
}
|
|
263
|
+
]
|
|
193
264
|
}
|
|
194
265
|
]
|
|
195
266
|
}
|
package/shared/spec/schema.js
CHANGED
|
@@ -154,3 +154,59 @@ media.register({
|
|
|
154
154
|
}
|
|
155
155
|
]
|
|
156
156
|
})
|
|
157
|
+
|
|
158
|
+
media.register({
|
|
159
|
+
name: 'crop-image-request',
|
|
160
|
+
fields: [
|
|
161
|
+
{
|
|
162
|
+
name: 'path',
|
|
163
|
+
type: 'string'
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'httpLink',
|
|
167
|
+
type: 'string'
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'buffer',
|
|
171
|
+
type: 'buffer'
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'mimetype',
|
|
175
|
+
type: 'string'
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'left',
|
|
179
|
+
type: 'uint'
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: 'top',
|
|
183
|
+
type: 'uint'
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'width',
|
|
187
|
+
type: 'uint'
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: 'height',
|
|
191
|
+
type: 'uint'
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: 'format',
|
|
195
|
+
type: 'string'
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
media.register({
|
|
201
|
+
name: 'crop-image-response',
|
|
202
|
+
fields: [
|
|
203
|
+
{
|
|
204
|
+
name: 'metadata',
|
|
205
|
+
type: '@media/metadata'
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'data',
|
|
209
|
+
type: 'buffer'
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
})
|
package/worker/index.js
CHANGED
package/worker/media.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import b4a from 'b4a'
|
|
2
|
-
import fs from 'bare-fs'
|
|
3
|
-
import fetch from 'bare-fetch'
|
|
4
2
|
|
|
5
3
|
import {
|
|
6
4
|
importCodec,
|
|
7
5
|
isCodecSupported,
|
|
8
6
|
supportsQuality
|
|
9
7
|
} from '../shared/codecs.js'
|
|
10
|
-
import { detectMimeType, calculateFitDimensions } from './util'
|
|
8
|
+
import { getBuffer, detectMimeType, calculateFitDimensions } from './util'
|
|
11
9
|
|
|
12
10
|
const DEFAULT_PREVIEW_FORMAT = 'image/webp'
|
|
13
11
|
|
|
@@ -143,21 +141,39 @@ export async function decodeImage({ path, httpLink, buffer, mimetype }) {
|
|
|
143
141
|
}
|
|
144
142
|
}
|
|
145
143
|
|
|
146
|
-
async function
|
|
147
|
-
|
|
144
|
+
export async function cropImage({
|
|
145
|
+
path,
|
|
146
|
+
httpLink,
|
|
147
|
+
buffer,
|
|
148
|
+
mimetype,
|
|
149
|
+
left,
|
|
150
|
+
top,
|
|
151
|
+
width,
|
|
152
|
+
height,
|
|
153
|
+
format
|
|
154
|
+
}) {
|
|
155
|
+
const buff = await getBuffer({ path, httpLink, buffer })
|
|
156
|
+
mimetype = mimetype || detectMimeType(buff, path)
|
|
148
157
|
|
|
149
|
-
if (
|
|
150
|
-
|
|
158
|
+
if (!isCodecSupported(mimetype)) {
|
|
159
|
+
throw new Error(`Unsupported file type: No codec available for ${mimetype}`)
|
|
151
160
|
}
|
|
152
161
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
162
|
+
const rgba = await decodeImageToRGBA(buff, mimetype)
|
|
163
|
+
|
|
164
|
+
const cropped = await cropRGBA(rgba, left, top, width, height)
|
|
157
165
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
const data = await encodeImageFromRGBA(cropped, format || mimetype)
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
metadata: {
|
|
170
|
+
dimensions: {
|
|
171
|
+
width: rgba.width,
|
|
172
|
+
height: rgba.height
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
data
|
|
176
|
+
}
|
|
161
177
|
}
|
|
162
178
|
|
|
163
179
|
async function decodeImageToRGBA(buffer, mimetype, maxFrames) {
|
|
@@ -227,3 +243,32 @@ async function resizeRGBA(rgba, maxWidth, maxHeight) {
|
|
|
227
243
|
|
|
228
244
|
return maybeResizedRGBA
|
|
229
245
|
}
|
|
246
|
+
|
|
247
|
+
async function cropRGBA(rgba, left, top, width, height) {
|
|
248
|
+
if (
|
|
249
|
+
left < 0 ||
|
|
250
|
+
top < 0 ||
|
|
251
|
+
width <= 0 ||
|
|
252
|
+
height <= 0 ||
|
|
253
|
+
left + width > rgba.width ||
|
|
254
|
+
top + height > rgba.height
|
|
255
|
+
) {
|
|
256
|
+
throw new Error('Crop rectangle out of bounds')
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const data = Buffer.alloc(width * height * 4)
|
|
260
|
+
|
|
261
|
+
for (let y = 0; y < height; y++) {
|
|
262
|
+
for (let x = 0; x < width; x++) {
|
|
263
|
+
const srcIndex = ((y + top) * rgba.width + (x + left)) * 4
|
|
264
|
+
const dstIndex = (y * width + x) * 4
|
|
265
|
+
|
|
266
|
+
data[dstIndex] = rgba.data[srcIndex]
|
|
267
|
+
data[dstIndex + 1] = rgba.data[srcIndex + 1]
|
|
268
|
+
data[dstIndex + 2] = rgba.data[srcIndex + 2]
|
|
269
|
+
data[dstIndex + 3] = rgba.data[srcIndex + 3]
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return { width, height, data }
|
|
274
|
+
}
|
package/worker/util.js
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
|
+
import fs from 'bare-fs'
|
|
2
|
+
import fetch from 'bare-fetch'
|
|
1
3
|
import getMimeType from 'get-mime-type'
|
|
2
4
|
import getFileFormat from 'get-file-format'
|
|
3
5
|
|
|
4
6
|
export const log = (...args) => console.log('[bare-media]', ...args)
|
|
5
7
|
|
|
8
|
+
export async function getBuffer({ path, httpLink, buffer }) {
|
|
9
|
+
if (buffer) return buffer
|
|
10
|
+
|
|
11
|
+
if (path) {
|
|
12
|
+
return fs.readFileSync(path)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (httpLink) {
|
|
16
|
+
const response = await fetch(httpLink)
|
|
17
|
+
return await response.buffer()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
throw new Error(
|
|
21
|
+
'At least one of "path", "httpLink" or "buffer" must be provided'
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
6
25
|
export function detectMimeType(buffer, path) {
|
|
7
26
|
return getMimeType(getFileFormat(buffer)) || getMimeType(path)
|
|
8
27
|
}
|