Skip to content

Commit

Permalink
Add Split function (#128)
Browse files Browse the repository at this point in the history
* Split function

* Add comment and test
  • Loading branch information
sunshineplan authored Sep 12, 2024
1 parent 196052d commit ba915b2
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 2 deletions.
2 changes: 1 addition & 1 deletion converter/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module converter

go 1.22
go 1.23

require (
github.com/sunshineplan/imgconv v0.0.0-00010101000000-000000000000
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/sunshineplan/imgconv

go 1.21
go 1.22

require (
github.com/sunshineplan/pdf v1.0.7
Expand Down
78 changes: 78 additions & 0 deletions split.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package imgconv

import (
"errors"
"image"
)

// SplitMode defines the mode in which the image will be split
type SplitMode int

const (
// SplitHorizontalMode splits the image horizontally
SplitHorizontalMode SplitMode = iota
// SplitVerticalMode splits the image vertically
SplitVerticalMode
)

func split(base image.Rectangle, n int, mode SplitMode) (rects []image.Rectangle) {
var width, height int
if mode == SplitHorizontalMode {
width = base.Dx() / n
height = base.Dy()
} else {
width = base.Dx()
height = base.Dy() / n
}
if width == 0 || height == 0 {
return
}
for i := range n {
var r image.Rectangle
if mode == SplitHorizontalMode {
r = image.Rect(
base.Min.X+width*i, base.Min.Y,
base.Min.X+width*(i+1), base.Min.Y+height,
)
} else {
r = image.Rect(
base.Min.X, base.Min.Y+height*i,
base.Min.X+width, base.Min.Y+height*(i+1),
)
}
rects = append(rects, r)
}
return
}

// Split splits an image into n smaller images based on the specified split mode.
// If n is less than 1, or the image cannot be split, it returns an error.
func Split(base image.Image, n int, mode SplitMode) (imgs []image.Image, err error) {
if n < 1 {
return nil, errors.New("invalid number of parts: must be at least 1")
}
if img, ok := base.(interface {
SubImage(image.Rectangle) image.Image
}); ok {
rects := split(base.Bounds(), n, mode)
if len(rects) == 0 {
return nil, errors.New("failed to split the image: invalid dimensions or n is too large")
}
for _, rect := range rects {
imgs = append(imgs, img.SubImage(rect))
}
} else {
return nil, errors.New("image type does not support SubImage extraction")
}
return
}

// SplitHorizontal splits an image into n parts horizontally.
func SplitHorizontal(base image.Image, n int) ([]image.Image, error) {
return Split(base, n, SplitHorizontalMode)
}

// SplitVertical splits an image into n parts vertically.
func SplitVertical(base image.Image, n int) ([]image.Image, error) {
return Split(base, n, SplitVerticalMode)
}
75 changes: 75 additions & 0 deletions split_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package imgconv

import (
"image"
"slices"
"testing"
)

func TestSplit(t *testing.T) {
for i, testcase := range []struct {
base image.Rectangle
n int
mode SplitMode
want []image.Rectangle
}{
{image.Rect(0, 0, 100, 100), 4, SplitHorizontalMode, []image.Rectangle{
image.Rect(0, 0, 25, 100),
image.Rect(25, 0, 50, 100),
image.Rect(50, 0, 75, 100),
image.Rect(75, 0, 100, 100),
}},
{image.Rect(0, 0, 100, 100), 4, SplitVerticalMode, []image.Rectangle{
image.Rect(0, 0, 100, 25),
image.Rect(0, 25, 100, 50),
image.Rect(0, 50, 100, 75),
image.Rect(0, 75, 100, 100),
}},
{image.Rect(100, 100, 200, 200), 4, SplitHorizontalMode, []image.Rectangle{
image.Rect(100, 100, 125, 200),
image.Rect(125, 100, 150, 200),
image.Rect(150, 100, 175, 200),
image.Rect(175, 100, 200, 200),
}},
{image.Rect(100, 100, 200, 200), 4, SplitVerticalMode, []image.Rectangle{
image.Rect(100, 100, 200, 125),
image.Rect(100, 125, 200, 150),
image.Rect(100, 150, 200, 175),
image.Rect(100, 175, 200, 200),
}},
} {
if rects := split(testcase.base, testcase.n, testcase.mode); slices.CompareFunc(
rects,
testcase.want,
func(a, b image.Rectangle) int {
if a.Eq(b) {
return 0
}
return 1
},
) != 0 {
t.Errorf("#%d wrong split results: want %v, got %v", i, testcase.want, rects)
}
}
}

func TestSplitError(t *testing.T) {
r := image.Rect(0, 0, 100, 100)
img := image.NewNRGBA(r)
if _, err := Split(img, 10, SplitHorizontalMode); err != nil {
t.Fatal(err)
}
for i, testcase := range []struct {
img image.Image
n int
}{
{r, 10},
{img, 0},
{img, 101},
{image.NewNRGBA(image.Rectangle{}), 10},
} {
if _, err := Split(testcase.img, testcase.n, SplitHorizontalMode); err == nil {
t.Errorf("#%d want error, got nil", i)
}
}
}

0 comments on commit ba915b2

Please sign in to comment.