Skip to content

Commit

Permalink
Merge pull request #20 from osspkg/develop
Browse files Browse the repository at this point in the history
moved pkg from github.com/osspkg/goppy
  • Loading branch information
markus621 authored Jul 1, 2024
2 parents 01bb22c + 0c870d9 commit 09e7547
Show file tree
Hide file tree
Showing 29 changed files with 1,373 additions and 6 deletions.
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

0 comments on commit 09e7547

Please sign in to comment.