• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cshum / imagor / 15233893740

25 May 2025 03:47AM UTC coverage: 92.118% (+2.4%) from 89.678%
15233893740

Pull #544

github

cshum
update exif extraction
Pull Request #544: feat: introducing vipsgen

308 of 346 new or added lines in 6 files covered. (89.02%)

6 existing lines in 1 file now uncovered.

4862 of 5278 relevant lines covered (92.12%)

1.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.7
/processor/vipsprocessor/filter.go
1
package vipsprocessor
2

3
import (
4
        "context"
5
        "fmt"
6
        "github.com/cshum/vipsgen/vips"
7
        "image/color"
8
        "math"
9
        "net/url"
10
        "strconv"
11
        "strings"
12

13
        "github.com/cshum/imagor"
14
        "github.com/cshum/imagor/imagorpath"
15
        "golang.org/x/image/colornames"
16
)
17

18
func (v *Processor) watermark(ctx context.Context, img *vips.Image, load imagor.LoadFunc, args ...string) (err error) {
1✔
19
        ln := len(args)
1✔
20
        if ln < 1 {
2✔
21
                return
1✔
22
        }
1✔
23
        image := args[0]
1✔
24
        if unescape, e := url.QueryUnescape(args[0]); e == nil {
2✔
25
                image = unescape
1✔
26
        }
1✔
27
        var blob *imagor.Blob
1✔
28
        if blob, err = load(image); err != nil {
1✔
29
                return
×
30
        }
×
31
        var x, y, w, h int
1✔
32
        var across = 1
1✔
33
        var down = 1
1✔
34
        var overlay *vips.Image
1✔
35
        var n = 1
1✔
36
        if isAnimated(img) {
2✔
37
                n = -1
1✔
38
        }
1✔
39
        // w_ratio h_ratio
40
        if ln >= 6 {
2✔
41
                w = img.Width()
1✔
42
                h = img.PageHeight()
1✔
43
                if args[4] != "none" {
2✔
44
                        w, _ = strconv.Atoi(args[4])
1✔
45
                        w = img.Width() * w / 100
1✔
46
                }
1✔
47
                if args[5] != "none" {
2✔
48
                        h, _ = strconv.Atoi(args[5])
1✔
49
                        h = img.PageHeight() * h / 100
1✔
50
                }
1✔
51
                if overlay, err = v.NewThumbnail(
1✔
52
                        ctx, blob, w, h, vips.InterestingNone, vips.SizeBoth, n, 1, 0,
1✔
53
                ); err != nil {
1✔
54
                        return
×
55
                }
×
56
        } else {
1✔
57
                if overlay, err = v.NewThumbnail(
1✔
58
                        ctx, blob, v.MaxWidth, v.MaxHeight, vips.InterestingNone, vips.SizeDown, n, 1, 0,
1✔
59
                ); err != nil {
1✔
60
                        return
×
61
                }
×
62
        }
63
        var overlayN = overlay.Height() / overlay.PageHeight()
1✔
64
        contextDefer(ctx, overlay.Close)
1✔
65
        if overlay.Bands() < 3 {
2✔
66
                if err = overlay.Colourspace(vips.InterpretationSrgb, nil); err != nil {
1✔
67
                        return
×
68
                }
×
69
        }
70
        if !overlay.HasAlpha() {
2✔
71
                if err = overlay.Addalpha(); err != nil {
1✔
NEW
72
                        return
×
NEW
73
                }
×
74
        }
75
        w = overlay.Width()
1✔
76
        h = overlay.PageHeight()
1✔
77
        // alpha
1✔
78
        if ln >= 4 {
2✔
79
                alpha, _ := strconv.ParseFloat(args[3], 64)
1✔
80
                alpha = 1 - alpha/100
1✔
81
                if alpha != 1 {
2✔
82
                        if err = overlay.Linear([]float64{1, 1, 1, alpha}, []float64{0, 0, 0, 0}, nil); err != nil {
1✔
83
                                return
×
84
                        }
×
85
                }
86
        }
87
        // x y
88
        if ln >= 3 {
2✔
89
                if args[1] == "center" {
2✔
90
                        x = (img.Width() - overlay.Width()) / 2
1✔
91
                } else if args[1] == imagorpath.HAlignLeft {
3✔
92
                        x = 0
1✔
93
                } else if args[1] == imagorpath.HAlignRight {
3✔
94
                        x = img.Width() - overlay.Width()
1✔
95
                } else if args[1] == "repeat" {
3✔
96
                        x = 0
1✔
97
                        across = img.Width()/overlay.Width() + 1
1✔
98
                } else if strings.HasPrefix(strings.TrimPrefix(args[1], "-"), "0.") {
3✔
99
                        pec, _ := strconv.ParseFloat(args[1], 64)
1✔
100
                        x = int(pec * float64(img.Width()))
1✔
101
                } else if strings.HasSuffix(args[1], "p") {
3✔
102
                        x, _ = strconv.Atoi(strings.TrimSuffix(args[1], "p"))
1✔
103
                        x = x * img.Width() / 100
1✔
104
                } else {
2✔
105
                        x, _ = strconv.Atoi(args[1])
1✔
106
                }
1✔
107
                if args[2] == "center" {
2✔
108
                        y = (img.PageHeight() - overlay.PageHeight()) / 2
1✔
109
                } else if args[2] == imagorpath.VAlignTop {
3✔
110
                        y = 0
1✔
111
                } else if args[2] == imagorpath.VAlignBottom {
3✔
112
                        y = img.PageHeight() - overlay.PageHeight()
1✔
113
                } else if args[2] == "repeat" {
3✔
114
                        y = 0
1✔
115
                        down = img.PageHeight()/overlay.PageHeight() + 1
1✔
116
                } else if strings.HasPrefix(strings.TrimPrefix(args[2], "-"), "0.") {
3✔
117
                        pec, _ := strconv.ParseFloat(args[2], 64)
1✔
118
                        y = int(pec * float64(img.PageHeight()))
1✔
119
                } else if strings.HasSuffix(args[2], "p") {
3✔
120
                        y, _ = strconv.Atoi(strings.TrimSuffix(args[2], "p"))
1✔
121
                        y = y * img.PageHeight() / 100
1✔
122
                } else {
2✔
123
                        y, _ = strconv.Atoi(args[2])
1✔
124
                }
1✔
125
                if x < 0 {
2✔
126
                        x += img.Width() - overlay.Width()
1✔
127
                }
1✔
128
                if y < 0 {
2✔
129
                        y += img.PageHeight() - overlay.PageHeight()
1✔
130
                }
1✔
131
        }
132
        if across*down > 1 {
2✔
133
                if err = overlay.EmbedMultiPage(0, 0, across*w, down*h,
1✔
134
                        &vips.EmbedMultiPageOptions{Extend: vips.ExtendRepeat}); err != nil {
1✔
135
                        return
×
136
                }
×
137
        }
138
        if err = overlay.EmbedMultiPage(
1✔
139
                x, y, img.Width(), img.PageHeight(), nil,
1✔
140
        ); err != nil {
1✔
141
                return
×
142
        }
×
143
        if n := img.Height() / img.PageHeight(); n > overlayN {
2✔
144
                cnt := n / overlayN
1✔
145
                if n%overlayN > 0 {
2✔
146
                        cnt++
1✔
147
                }
1✔
148
                if err = overlay.Replicate(1, cnt); err != nil {
1✔
149
                        return
×
150
                }
×
151
        }
152
        if err = img.Composite2(overlay, vips.BlendModeOver, nil); err != nil {
1✔
153
                return
×
154
        }
×
155
        return
1✔
156
}
157

158
func setFrames(_ context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
159
        ln := len(args)
1✔
160
        if ln == 0 {
2✔
161
                return
1✔
162
        }
1✔
163
        newN, _ := strconv.Atoi(args[0])
1✔
164
        if newN < 1 {
2✔
165
                return
1✔
166
        }
1✔
167
        if n := img.Height() / img.PageHeight(); n != newN {
2✔
168
                height := img.PageHeight()
1✔
169
                if err = img.SetPageHeight(img.Height()); err != nil {
1✔
170
                        return
×
171
                }
×
172
                if err = img.EmbedMultiPage(0, 0, img.Width(), height*newN, &vips.EmbedMultiPageOptions{
1✔
173
                        Extend: vips.ExtendRepeat,
1✔
174
                }); err != nil {
1✔
175
                        return
×
176
                }
×
177
                if err = img.SetPageHeight(height); err != nil {
1✔
178
                        return
×
179
                }
×
180
        }
181
        var delay int
1✔
182
        if ln > 1 {
2✔
183
                delay, _ = strconv.Atoi(args[1])
1✔
184
        }
1✔
185
        if delay == 0 {
2✔
186
                delay = 100
1✔
187
        }
1✔
188
        delays := make([]int, newN)
1✔
189
        for i := 0; i < newN; i++ {
2✔
190
                delays[i] = delay
1✔
191
        }
1✔
192
        if err = img.SetPageDelay(delays); err != nil {
1✔
193
                return
×
194
        }
×
195
        return
1✔
196
}
197

198
func (v *Processor) fill(ctx context.Context, img *vips.Image, w, h int, pLeft, pTop, pRight, pBottom int, colour string) (err error) {
1✔
199
        if isRotate90(ctx) {
2✔
200
                tmpW := w
1✔
201
                w = h
1✔
202
                h = tmpW
1✔
203
                tmpPLeft := pLeft
1✔
204
                pLeft = pTop
1✔
205
                pTop = tmpPLeft
1✔
206
                tmpPRight := pRight
1✔
207
                pRight = pBottom
1✔
208
                pBottom = tmpPRight
1✔
209
        }
1✔
210
        c := getColor(img, colour)
1✔
211
        left := (w-img.Width())/2 + pLeft
1✔
212
        top := (h-img.PageHeight())/2 + pTop
1✔
213
        width := w + pLeft + pRight
1✔
214
        height := h + pTop + pBottom
1✔
215
        if colour != "blur" || v.DisableBlur || isAnimated(img) {
2✔
216
                // fill color
1✔
217
                isTransparent := colour == "none" || colour == "transparent"
1✔
218
                if img.HasAlpha() && !isTransparent {
2✔
219
                        c := getColor(img, colour)
1✔
220
                        if err = img.Flatten(&vips.FlattenOptions{Background: []float64{
1✔
221
                                c.R, c.G, c.B,
1✔
222
                        }}); err != nil {
1✔
223
                                return
×
224
                        }
×
225
                }
226
                if isTransparent {
2✔
227
                        if img.Bands() < 3 {
2✔
228
                                if err = img.Colourspace(vips.InterpretationSrgb, nil); err != nil {
1✔
229
                                        return
×
230
                                }
×
231
                        }
232
                        if !img.HasAlpha() {
2✔
233
                                if err = img.Addalpha(); err != nil {
1✔
NEW
234
                                        return
×
NEW
235
                                }
×
236
                        }
237
                        if err = img.EmbedMultiPage(left, top, width, height, &vips.EmbedMultiPageOptions{Extend: vips.ExtendBlack}); err != nil {
1✔
238
                                return
×
239
                        }
×
240
                } else if isBlack(c) {
2✔
241
                        if err = img.EmbedMultiPage(left, top, width, height, &vips.EmbedMultiPageOptions{Extend: vips.ExtendBlack}); err != nil {
1✔
242
                                return
×
243
                        }
×
244
                } else if isWhite(c) {
2✔
245
                        if err = img.EmbedMultiPage(left, top, width, height, &vips.EmbedMultiPageOptions{Extend: vips.ExtendWhite}); err != nil {
1✔
246
                                return
×
247
                        }
×
248
                } else {
1✔
249
                        if err = img.EmbedMultiPage(left, top, width, height, &vips.EmbedMultiPageOptions{
1✔
250
                                Extend:     vips.ExtendBackground,
1✔
251
                                Background: []float64{c.R, c.G, c.B},
1✔
252
                        }); err != nil {
1✔
253
                                return
×
254
                        }
×
255
                }
256
        } else {
1✔
257
                // fill blur
1✔
258
                var cp *vips.Image
1✔
259
                if cp, err = img.Copy(nil); err != nil {
1✔
260
                        return
×
261
                }
×
262
                contextDefer(ctx, cp.Close)
1✔
263
                if err = img.ThumbnailImage(
1✔
264
                        width, &vips.ThumbnailImageOptions{
1✔
265
                                Height: height,
1✔
266
                                Crop:   vips.InterestingNone,
1✔
267
                                Size:   vips.SizeForce,
1✔
268
                        },
1✔
269
                ); err != nil {
1✔
270
                        return
×
271
                }
×
272
                if err = img.Gaussblur(50, nil); err != nil {
1✔
273
                        return
×
274
                }
×
275
                if err = img.Composite2(
1✔
276
                        cp, vips.BlendModeOver,
1✔
277
                        &vips.Composite2Options{X: left, Y: top}); err != nil {
1✔
278
                        return
×
279
                }
×
280
        }
281
        return
1✔
282
}
283

284
func roundCorner(ctx context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
285
        var rx, ry int
1✔
286
        var c *Color
1✔
287
        if len(args) == 0 {
2✔
288
                return
1✔
289
        }
1✔
290
        if a, e := url.QueryUnescape(args[0]); e == nil {
2✔
291
                args[0] = a
1✔
292
        }
1✔
293
        if len(args) == 3 {
2✔
294
                // rx,ry,color
1✔
295
                c = getColor(img, args[2])
1✔
296
                args = args[:2]
1✔
297
        }
1✔
298
        rx, _ = strconv.Atoi(args[0])
1✔
299
        ry = rx
1✔
300
        if len(args) > 1 {
2✔
301
                ry, _ = strconv.Atoi(args[1])
1✔
302
        }
1✔
303

304
        var rounded *vips.Image
1✔
305
        var w = img.Width()
1✔
306
        var h = img.PageHeight()
1✔
307
        if rounded, err = vips.NewSvgloadBuffer([]byte(fmt.Sprintf(`
1✔
308
                <svg viewBox="0 0 %d %d">
1✔
309
                        <rect rx="%d" ry="%d" 
1✔
310
                         x="0" y="0" width="%d" height="%d" 
1✔
311
                         fill="#fff"/>
1✔
312
                </svg>
1✔
313
        `, w, h, rx, ry, w, h)), nil); err != nil {
1✔
314
                return
×
315
        }
×
316
        contextDefer(ctx, rounded.Close)
1✔
317
        if n := img.Height() / img.PageHeight(); n > 1 {
2✔
318
                if err = rounded.Replicate(1, n); err != nil {
1✔
319
                        return
×
320
                }
×
321
        }
322
        if err = img.Composite2(rounded, vips.BlendModeDestIn, nil); err != nil {
1✔
323
                return
×
324
        }
×
325
        if c != nil {
2✔
326
                if err = img.Flatten(&vips.FlattenOptions{
1✔
327
                        Background: []float64{c.R, c.G, c.B},
1✔
328
                }); err != nil {
1✔
329
                        return
×
330
                }
×
331
        }
332
        return nil
1✔
333
}
334

335
func label(_ context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
336
        ln := len(args)
1✔
337
        if ln == 0 {
1✔
338
                return
×
339
        }
×
340
        if a, e := url.QueryUnescape(args[0]); e == nil {
2✔
341
                args[0] = a
1✔
342
        }
1✔
343
        var text = args[0]
1✔
344
        var font = "tahoma"
1✔
345
        var x, y int
1✔
346
        var c = &Color{}
1✔
347
        var alpha float64
1✔
348
        var align = vips.AlignLow
1✔
349
        var size = 20
1✔
350
        var width = img.Width()
1✔
351
        if ln > 3 {
2✔
352
                size, _ = strconv.Atoi(args[3])
1✔
353
        }
1✔
354
        if ln > 1 {
2✔
355
                if args[1] == "center" {
2✔
356
                        align = vips.AlignCentre
1✔
357
                        x = width / 2
1✔
358
                } else if args[1] == imagorpath.HAlignRight {
3✔
359
                        align = vips.AlignHigh
1✔
360
                        x = width
1✔
361
                } else if strings.HasPrefix(strings.TrimPrefix(args[1], "-"), "0.") {
3✔
362
                        pec, _ := strconv.ParseFloat(args[1], 64)
1✔
363
                        x = int(pec * float64(width))
1✔
364
                } else if strings.HasSuffix(args[1], "p") {
3✔
365
                        x, _ = strconv.Atoi(strings.TrimSuffix(args[1], "p"))
1✔
366
                        x = x * width / 100
1✔
367
                } else {
2✔
368
                        x, _ = strconv.Atoi(args[1])
1✔
369
                }
1✔
370
                if x < 0 {
2✔
371
                        align = vips.AlignHigh
1✔
372
                        x += width
1✔
373
                }
1✔
374
        }
375
        if ln > 2 {
2✔
376
                if args[2] == "center" {
2✔
377
                        y = (img.PageHeight() - size) / 2
1✔
378
                } else if args[2] == imagorpath.VAlignTop {
3✔
379
                        y = 0
1✔
380
                } else if args[2] == imagorpath.VAlignBottom {
3✔
381
                        y = img.PageHeight() - size
1✔
382
                } else if strings.HasPrefix(strings.TrimPrefix(args[2], "-"), "0.") {
3✔
383
                        pec, _ := strconv.ParseFloat(args[2], 64)
1✔
384
                        y = int(pec * float64(img.PageHeight()))
1✔
385
                } else if strings.HasSuffix(args[2], "p") {
3✔
386
                        y, _ = strconv.Atoi(strings.TrimSuffix(args[2], "p"))
1✔
387
                        y = y * img.PageHeight() / 100
1✔
388
                } else {
2✔
389
                        y, _ = strconv.Atoi(args[2])
1✔
390
                }
1✔
391
                if y < 0 {
2✔
392
                        y += img.PageHeight() - size
1✔
393
                }
1✔
394
        }
395
        if ln > 4 {
2✔
396
                c = getColor(img, args[4])
1✔
397
        }
1✔
398
        if ln > 5 {
2✔
399
                alpha, _ = strconv.ParseFloat(args[5], 64)
1✔
400
                alpha /= 100
1✔
401
        }
1✔
402
        if ln > 6 {
2✔
403
                if a, e := url.QueryUnescape(args[6]); e == nil {
2✔
404
                        font = a
1✔
405
                } else {
1✔
406
                        font = args[6]
×
407
                }
×
408
        }
409
        if img.Bands() < 3 {
2✔
410
                if err = img.Colourspace(vips.InterpretationSrgb, nil); err != nil {
1✔
411
                        return
×
412
                }
×
413
        }
414
        if !img.HasAlpha() {
2✔
415
                if err = img.Addalpha(); err != nil {
1✔
NEW
416
                        return
×
NEW
417
                }
×
418
        }
419
        return img.Label(text, x, y, &vips.LabelOptions{
1✔
420
                Font:    font,
1✔
421
                Size:    size,
1✔
422
                Align:   align,
1✔
423
                Opacity: 1 - alpha,
1✔
424
                Color:   []float64{c.R, c.G, c.B},
1✔
425
        })
1✔
426
}
427

428
func (v *Processor) padding(ctx context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) error {
1✔
429
        ln := len(args)
1✔
430
        if ln < 2 {
2✔
431
                return nil
1✔
432
        }
1✔
433
        var (
1✔
434
                c       = args[0]
1✔
435
                left, _ = strconv.Atoi(args[1])
1✔
436
                top     = left
1✔
437
                right   = left
1✔
438
                bottom  = left
1✔
439
        )
1✔
440
        if ln > 2 {
2✔
441
                top, _ = strconv.Atoi(args[2])
1✔
442
                bottom = top
1✔
443
        }
1✔
444
        if ln > 4 {
2✔
445
                right, _ = strconv.Atoi(args[3])
1✔
446
                bottom, _ = strconv.Atoi(args[4])
1✔
447
        }
1✔
448
        return v.fill(ctx, img, img.Width(), img.PageHeight(), left, top, right, bottom, c)
1✔
449
}
450

451
func backgroundColor(_ context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
452
        if len(args) == 0 {
2✔
453
                return
1✔
454
        }
1✔
455
        if !img.HasAlpha() {
2✔
456
                return
1✔
457
        }
1✔
458
        c := getColor(img, args[0])
1✔
459
        return img.Flatten(&vips.FlattenOptions{
1✔
460
                Background: []float64{c.R, c.G, c.B},
1✔
461
        })
1✔
462
}
463

464
func rotate(ctx context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
465
        if len(args) == 0 {
2✔
466
                return
1✔
467
        }
1✔
468
        if angle, _ := strconv.Atoi(args[0]); angle > 0 {
2✔
469
                switch angle {
1✔
470
                case 90, 270:
1✔
471
                        setRotate90(ctx)
1✔
472
                }
473
                if err = img.RotMultiPage(getAngle(angle)); err != nil {
1✔
474
                        return err
×
475
                }
×
476
        }
477
        return
1✔
478
}
479

480
func getAngle(angle int) vips.Angle {
1✔
481
        switch angle {
1✔
482
        case 90:
1✔
483
                return vips.AngleD270
1✔
484
        case 180:
1✔
485
                return vips.AngleD180
1✔
486
        case 270:
1✔
487
                return vips.AngleD90
1✔
488
        default:
×
NEW
489
                return vips.AngleD0
×
490
        }
491
}
492

493
func proportion(_ context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
494
        if len(args) == 0 {
2✔
495
                return
1✔
496
        }
1✔
497
        scale, _ := strconv.ParseFloat(args[0], 64)
1✔
498
        if scale <= 0 {
2✔
499
                return // no ops
1✔
500
        }
1✔
501
        if scale > 100 {
2✔
502
                scale = 100
1✔
503
        }
1✔
504
        if scale > 1 {
2✔
505
                scale /= 100
1✔
506
        }
1✔
507
        width := int(float64(img.Width()) * scale)
1✔
508
        height := int(float64(img.PageHeight()) * scale)
1✔
509
        if width <= 0 || height <= 0 {
2✔
510
                return // op ops
1✔
511
        }
1✔
512
        return img.ThumbnailImage(width, &vips.ThumbnailImageOptions{
1✔
513
                Height: height,
1✔
514
                Crop:   vips.InterestingNone,
1✔
515
        })
1✔
516
}
517

518
func grayscale(_ context.Context, img *vips.Image, _ imagor.LoadFunc, _ ...string) (err error) {
1✔
519
        return img.Colourspace(vips.InterpretationBW, nil)
1✔
520
}
1✔
521

522
func brightness(_ context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
523
        if len(args) == 0 {
2✔
524
                return
1✔
525
        }
1✔
526
        b, _ := strconv.ParseFloat(args[0], 64)
1✔
527
        b = b * 255 / 100
1✔
528
        return linearRGB(img, []float64{1, 1, 1}, []float64{b, b, b})
1✔
529
}
530

531
func contrast(_ context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
532
        if len(args) == 0 {
2✔
533
                return
1✔
534
        }
1✔
535
        a, _ := strconv.ParseFloat(args[0], 64)
1✔
536
        a = a * 255 / 100
1✔
537
        a = math.Min(math.Max(a, -255), 255)
1✔
538
        a = (259 * (a + 255)) / (255 * (259 - a))
1✔
539
        b := 128 - a*128
1✔
540
        return linearRGB(img, []float64{a, a, a}, []float64{b, b, b})
1✔
541
}
542

543
func hue(_ context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
544
        if len(args) == 0 {
2✔
545
                return
1✔
546
        }
1✔
547
        h, _ := strconv.ParseFloat(args[0], 64)
1✔
548
        return img.Modulate(1, 1, h)
1✔
549
}
550

551
func saturation(_ context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
552
        if len(args) == 0 {
2✔
553
                return
1✔
554
        }
1✔
555
        s, _ := strconv.ParseFloat(args[0], 64)
1✔
556
        s = 1 + s/100
1✔
557
        return img.Modulate(1, s, 0)
1✔
558
}
559

560
func rgb(_ context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
561
        if len(args) != 3 {
2✔
562
                return
1✔
563
        }
1✔
564
        r, _ := strconv.ParseFloat(args[0], 64)
1✔
565
        g, _ := strconv.ParseFloat(args[1], 64)
1✔
566
        b, _ := strconv.ParseFloat(args[2], 64)
1✔
567
        r = r * 255 / 100
1✔
568
        g = g * 255 / 100
1✔
569
        b = b * 255 / 100
1✔
570
        return linearRGB(img, []float64{1, 1, 1}, []float64{r, g, b})
1✔
571
}
572

573
func modulate(_ context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
574
        if len(args) != 3 {
2✔
575
                return
1✔
576
        }
1✔
577
        b, _ := strconv.ParseFloat(args[0], 64)
1✔
578
        s, _ := strconv.ParseFloat(args[1], 64)
1✔
579
        h, _ := strconv.ParseFloat(args[2], 64)
1✔
580
        b = 1 + b/100
1✔
581
        s = 1 + s/100
1✔
582
        return img.Modulate(b, s, h)
1✔
583
}
584

585
func blur(ctx context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
586
        if isAnimated(img) {
2✔
587
                // skip animation support
1✔
588
                return
1✔
589
        }
1✔
590
        var sigma float64
1✔
591
        switch len(args) {
1✔
592
        case 2:
1✔
593
                sigma, _ = strconv.ParseFloat(args[1], 64)
1✔
594
                break
1✔
595
        case 1:
1✔
596
                sigma, _ = strconv.ParseFloat(args[0], 64)
1✔
597
                break
1✔
598
        }
599
        sigma /= 2
1✔
600
        if sigma > 0 {
2✔
601
                return img.Gaussblur(sigma, nil)
1✔
602
        }
1✔
603
        return
×
604
}
605

606
func sharpen(ctx context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) (err error) {
1✔
607
        if isAnimated(img) {
2✔
608
                // skip animation support
1✔
609
                return
1✔
610
        }
1✔
611
        var sigma float64
1✔
612
        switch len(args) {
1✔
613
        case 1:
1✔
614
                sigma, _ = strconv.ParseFloat(args[0], 64)
1✔
615
                break
1✔
616
        case 2, 3:
1✔
617
                sigma, _ = strconv.ParseFloat(args[1], 64)
1✔
618
                break
1✔
619
        }
620
        sigma = 1 + sigma*2
1✔
621
        if sigma > 0 {
2✔
622
                return img.Sharpen(&vips.SharpenOptions{
1✔
623
                        Sigma: sigma,
1✔
624
                        X1:    1,
1✔
625
                        M2:    2,
1✔
626
                })
1✔
627
        }
1✔
628
        return
1✔
629
}
630

631
func stripIcc(_ context.Context, img *vips.Image, _ imagor.LoadFunc, _ ...string) (err error) {
1✔
632
        return img.RemoveICCProfile()
1✔
633
}
1✔
634

635
func stripExif(_ context.Context, img *vips.Image, _ imagor.LoadFunc, _ ...string) (err error) {
1✔
636
        return img.RemoveExif()
1✔
637
}
1✔
638

639
func trim(ctx context.Context, img *vips.Image, _ imagor.LoadFunc, args ...string) error {
1✔
640
        var (
1✔
641
                ln        = len(args)
1✔
642
                pos       string
1✔
643
                tolerance int
1✔
644
        )
1✔
645
        if ln > 0 {
2✔
646
                tolerance, _ = strconv.Atoi(args[0])
1✔
647
        }
1✔
648
        if ln > 1 {
2✔
649
                pos = args[1]
1✔
650
        }
1✔
651
        if l, t, w, h, err := findTrim(ctx, img, pos, tolerance); err == nil {
2✔
652
                return img.ExtractAreaMultiPage(l, t, w, h)
1✔
653
        }
1✔
654
        return nil
×
655
}
656

657
func linearRGB(img *vips.Image, a, b []float64) error {
1✔
658
        if img.HasAlpha() {
2✔
659
                a = append(a, 1)
1✔
660
                b = append(b, 0)
1✔
661
        }
1✔
662
        return img.Linear(a, b, nil)
1✔
663
}
664

665
func isBlack(c *Color) bool {
1✔
666
        return c.R == 0x00 && c.G == 0x00 && c.B == 0x00
1✔
667
}
1✔
668

669
func isWhite(c *Color) bool {
1✔
670
        return c.R == 0xff && c.G == 0xff && c.B == 0xff
1✔
671
}
1✔
672

673
func getColor(img *vips.Image, color string) *Color {
1✔
674
        vc := &Color{}
1✔
675
        args := strings.Split(strings.ToLower(color), ",")
1✔
676
        mode := ""
1✔
677
        name := strings.TrimPrefix(args[0], "#")
1✔
678
        if len(args) > 1 {
2✔
679
                mode = args[1]
1✔
680
        }
1✔
681
        if name == "auto" {
2✔
682
                if img != nil {
2✔
683
                        x := 0
1✔
684
                        y := 0
1✔
685
                        if mode == "bottom-right" {
2✔
686
                                x = img.Width() - 1
1✔
687
                                y = img.PageHeight() - 1
1✔
688
                        }
1✔
689
                        p, _ := img.Getpoint(x, y, nil)
1✔
690
                        if len(p) >= 3 {
2✔
691
                                vc.R = p[0]
1✔
692
                                vc.G = p[1]
1✔
693
                                vc.B = p[2]
1✔
694
                        }
1✔
695
                }
696
        } else if c, ok := colornames.Map[name]; ok {
2✔
697
                vc.R = float64(c.R)
1✔
698
                vc.G = float64(c.G)
1✔
699
                vc.B = float64(c.B)
1✔
700
        } else if c, ok := parseHexColor(name); ok {
3✔
701
                vc.R = float64(c.R)
1✔
702
                vc.G = float64(c.G)
1✔
703
                vc.B = float64(c.B)
1✔
704
        }
1✔
705
        return vc
1✔
706
}
707

708
func parseHexColor(s string) (c color.RGBA, ok bool) {
1✔
709
        c.A = 0xff
1✔
710
        switch len(s) {
1✔
711
        case 6:
1✔
712
                c.R = hexToByte(s[0])<<4 + hexToByte(s[1])
1✔
713
                c.G = hexToByte(s[2])<<4 + hexToByte(s[3])
1✔
714
                c.B = hexToByte(s[4])<<4 + hexToByte(s[5])
1✔
715
                ok = true
1✔
716
        case 3:
1✔
717
                c.R = hexToByte(s[0]) * 17
1✔
718
                c.G = hexToByte(s[1]) * 17
1✔
719
                c.B = hexToByte(s[2]) * 17
1✔
720
                ok = true
1✔
721
        }
722
        return
1✔
723
}
724

725
func hexToByte(b byte) byte {
1✔
726
        switch {
1✔
727
        case b >= '0' && b <= '9':
1✔
728
                return b - '0'
1✔
729
        case b >= 'a' && b <= 'f':
1✔
730
                return b - 'a' + 10
1✔
731
        }
732
        return 0
1✔
733
}
734

735
func isAnimated(img *vips.Image) bool {
1✔
736
        return img.Height() > img.PageHeight()
1✔
737
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc