Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

moved pkg from github.com/osspkg/goppy #20

Merged
merged 1 commit into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ go 1.20
replace go.osspkg.com/x/test => ./../test

require (
go.osspkg.com/x/test v0.3.0
go.osspkg.com/x/test v0.5.0
gopkg.in/yaml.v3 v3.0.1
)
2 changes: 1 addition & 1 deletion console/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ go 1.20

replace go.osspkg.com/x/errors => ../errors

require go.osspkg.com/x/errors v0.3.2
require go.osspkg.com/x/errors v0.5.0
32 changes: 32 additions & 0 deletions context/combine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2019-2024 Mikhail Knyazhev <[email protected]>. All rights reserved.
* Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file.
*/

package context

import (
cc "context"
"reflect"
)

func Join(multi ...cc.Context) (cc.Context, cc.CancelFunc) {
ctx, cancel := cc.WithCancel(cc.Background())

go func() {
cases := make([]reflect.SelectCase, 0, len(multi))
for _, vv := range multi {
cases = append(cases, reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(vv.Done()),
})
}
chosen, _, _ := reflect.Select(cases)
switch chosen {
default:
cancel()
}
}()

return ctx, cancel
}
39 changes: 39 additions & 0 deletions context/combine_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2024 Mikhail Knyazhev <[email protected]>. All rights reserved.
* Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file.
*/

package context

import (
"context"
"fmt"
"testing"
"time"
)

func TestUnit_Join(t *testing.T) {
c, cancel := Join(context.Background(), context.Background())
if c == nil {
t.Fatalf("contexts.Join returned nil")
}

select {
case <-c.Done():
t.Fatalf("<-c.Done() == it should block")
default:
}

cancel()
<-time.After(time.Second)

select {
case <-c.Done():
default:
t.Fatalf("<-c.Done() it shouldn't block")
}

if got, want := fmt.Sprint(c), "context.Background.WithCancel"; got != want {
t.Fatalf("contexts.Join() = %q want %q", got, want)
}
}
52 changes: 52 additions & 0 deletions context/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2019-2024 Mikhail Knyazhev <[email protected]>. All rights reserved.
* Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file.
*/

package context

import cc "context"

type (
_ctx struct {
ctx cc.Context
cancel cc.CancelFunc
}

Context interface {
Close()
Context() cc.Context
Done() <-chan struct{}
}
)

func New() Context {
ctx, cancel := cc.WithCancel(cc.Background())
return &_ctx{
ctx: ctx,
cancel: cancel,
}
}

func NewContext(c cc.Context) Context {
ctx, cancel := cc.WithCancel(c)
return &_ctx{
ctx: ctx,
cancel: cancel,
}
}

// Close context close method
func (v *_ctx) Close() {
v.cancel()
}

// Context general context
func (v *_ctx) Context() cc.Context {
return v.ctx
}

// Done context close wait channel
func (v *_ctx) Done() <-chan struct{} {
return v.ctx.Done()
}
3 changes: 3 additions & 0 deletions context/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module go.osspkg.com/x/context

go 1.20
Empty file added context/go.sum
Empty file.
4 changes: 4 additions & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ use (
./algorithms
./config
./console
./context
./domain
./env
./errors
./graphics
./io
./log
./random
./routine
./sync
./syscall
./test
Expand Down
4 changes: 4 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
10 changes: 10 additions & 0 deletions graphics/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module go.osspkg.com/x/graphics

go 1.20

replace go.osspkg.com/x/errors => ../errors

require (
go.osspkg.com/x/errors v0.5.0
golang.org/x/image v0.18.0
)
2 changes: 2 additions & 0 deletions graphics/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
184 changes: 184 additions & 0 deletions graphics/images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* Copyright (c) 2019-2024 Mikhail Knyazhev <[email protected]>. All rights reserved.
* Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file.
*/

package graphics

import (
"crypto/sha1"
"fmt"
"image"
"image/jpeg"
"image/png"
"io"
"os"
"path/filepath"
"strings"
"sync"

"go.osspkg.com/x/errors"
"golang.org/x/image/bmp"
"golang.org/x/image/draw"
"golang.org/x/image/tiff"
"golang.org/x/image/webp"
)

var (
ExtNotSupported = errors.New("ext is not supported")
)

type (
ImageScale struct {
folder string
cache map[string]ImageInfo
mux sync.Mutex
}

ImageInfo struct {
Hash string
Origin string
Scale string
Thumb string
}
)

var decoders = map[string]func(r io.Reader) (image.Image, error){
".jpg": jpeg.Decode,
".jpeg": jpeg.Decode,
".png": png.Decode,
".webp": webp.Decode,
".bmp": bmp.Decode,
".tiff": tiff.Decode,
}

func NewImageScale() *ImageScale {
return &ImageScale{
folder: "",
cache: make(map[string]ImageInfo, 100),
}
}

func (v *ImageScale) SetFolder(dir string) error {
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("create image folder: %w", err)
}
v.folder = dir
return nil
}

func (v *ImageScale) Build(filename string, scale, thumb int) (*ImageInfo, error) {
var err error
img := &ImageInfo{}

if img.Hash, err = v.getHash(filename); err != nil {
return nil, err
}

v.mux.Lock()
i, ok := v.cache[img.Hash]
v.mux.Unlock()
if ok {
return &i, nil
}

if img.Origin, err = v.resize(filename, img.Hash+".orig", 0); err != nil {
return nil, err
}
if img.Scale, err = v.resize(filename, img.Hash+".scale", scale); err != nil {
return nil, err
}
if img.Thumb, err = v.resize(filename, img.Hash+".thumb", thumb); err != nil {
return nil, err
}

v.mux.Lock()
v.cache[img.Hash] = *img
v.mux.Unlock()

return img, nil
}

func (v *ImageScale) resize(filename, suffix string, width int) (string, error) {
src, name, err := v.readFile(filename)
if err != nil {
return "", err
}
x, y := v.scaleFactor(src.Bounds().Max.X, src.Bounds().Max.Y, width)
dst := image.NewNRGBA(image.Rect(0, 0, x, y))
draw.CatmullRom.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
newFilename := fmt.Sprintf("%s-%s.png", name, suffix)
return newFilename, v.writeFile(v.folder+"/"+newFilename, dst)
}

func (v *ImageScale) scaleFactor(oW, oH, width int) (int, int) {
if width == 0 {
return oW, oH
}
oWF, oHF := float64(oW), float64(oH)
nWidth := float64(width)
scale := oWF / nWidth
return int(oWF / scale), int(oHF / scale)
}

func (v *ImageScale) writeFile(filename string, img image.Image) error {
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("write image `%s`: %w", filename, err)
}
if err = png.Encode(file, img); err != nil {
return errors.Wrap(
fmt.Errorf("encode image `%s`: %w", filename, err),
file.Close(),
)
}
return file.Close()
}

func (v *ImageScale) readFile(filename string) (image.Image, string, error) {
ext := filepath.Ext(filename)
dec, ok := decoders[ext]
if !ok {
return nil, "", ExtNotSupported
}
file, err := os.Open(filename)
if err != nil {
return nil, "", fmt.Errorf("read image `%s`: %w", filename, err)
}
img, err := dec(file)
if err != nil {
return nil, "", errors.Wrap(
fmt.Errorf("decode image `%s`: %w", filename, err),
file.Close(),
)
}
fi, err := file.Stat()
if err != nil {
return nil, "", errors.Wrap(
fmt.Errorf("info image `%s`: %w", filename, err),
file.Close(),
)
}
if err = file.Close(); err != nil {
return nil, "", fmt.Errorf("close image `%s`: %w", filename, err)
}
return img, strings.Replace(fi.Name(), ext, "", 1), nil
}

func (v *ImageScale) getHash(filename string) (string, error) {
file, err := os.Open(filename)
if err != nil {
return "", fmt.Errorf("read image `%s`: %w", filename, err)
}
h := sha1.New()
if _, err = io.Copy(h, file); err != nil {
return "", errors.Wrap(
fmt.Errorf("calc hash image `%s`: %w", filename, err),
file.Close(),
)
}
if err = file.Close(); err != nil {
return "", fmt.Errorf("close image `%s`: %w", filename, err)
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
6 changes: 3 additions & 3 deletions io/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ replace (
)

require (
go.osspkg.com/x/errors v0.3.1
go.osspkg.com/x/sync v0.3.0
go.osspkg.com/x/test v0.3.0
go.osspkg.com/x/errors v0.5.0
go.osspkg.com/x/sync v0.5.0
go.osspkg.com/x/test v0.5.0
gopkg.in/yaml.v3 v3.0.1
)
Loading
Loading