Skip to content

Commit

Permalink
feat: Implement AddText function, test and example
Browse files Browse the repository at this point in the history
  • Loading branch information
GhiaC committed Nov 3, 2019
1 parent 5a9d7f2 commit 4031199
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 0 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ If you're using `gopkg.in`, you can still rely in the `v0` without worrying abou
- Thumbnail
- Extract area
- Watermark (using text or image)
- AddText (support Persian)
- Gaussian blur effect
- Custom output color space (RGB, grayscale...)
- Format conversion (with additional quality/compression settings)
Expand Down Expand Up @@ -265,6 +266,34 @@ if err != nil {
bimg.Write("new.jpg", newImage)
```

#### AddText

```go
buffer, err := bimg.Read("image.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
}

addText := bimg.AddText{
Text: "Hello | سلام",
Top: 50,
Left: 50,
Opacity: 0.25,
Width: 200,
DPI: 100,
Margin: 150,
Font: "sans bold 12",
Background: bimg.Color{255, 255, 255},
}

newImage, err := bimg.NewImage(buffer).AddText(addText)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}

bimg.Write("new.jpg", newImage)
```

#### Fluent interface

```go
Expand Down
6 changes: 6 additions & 0 deletions image.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ func (i *Image) Watermark(w Watermark) ([]byte, error) {
return i.Process(options)
}

// adds text on the given image.
func (i *Image) AddText(a AddText) ([]byte, error) {
options := Options{AddText: a}
return i.Process(options)
}

// WatermarkImage adds image as watermark on the given image.
func (i *Image) WatermarkImage(w WatermarkImage) ([]byte, error) {
options := Options{WatermarkImage: w}
Expand Down
32 changes: 32 additions & 0 deletions image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,38 @@ func TestImageWatermark(t *testing.T) {

Write("testdata/test_watermark_text_out.jpg", buf)
}
func TestImageAddText(t *testing.T) {
image := initImage("test.jpg")
_, err := image.Crop(800, 600, GravityNorth)
if err != nil {
t.Errorf("Cannot process the image: %#v", err)
}

buf, err := image.AddText(AddText{
Width: 800,
Height: 600,
DPI: 100,
Top: 10,
Left: 500,
Text: "Copy me if you can",
Background: Color{255, 255, 255},
Opacity: 1,
})
if err != nil {
t.Error(err)
}

err = assertSize(buf, 800, 600)
if err != nil {
t.Error(err)
}

if DetermineImageType(buf) != JPEG {
t.Fatal("Image is not jpeg")
}

Write("test_add_text_out.jpg", buf)
}

func TestImageWatermarkWithImage(t *testing.T) {
image := initImage("test.jpg")
Expand Down
14 changes: 14 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,19 @@ type Watermark struct {
Background Color
}

// AddText represents the add-text options.
type AddText struct {
Width int
Height int
DPI int
Top int
Left int
Text string
Font string
Background Color
Opacity float32
}

// WatermarkImage represents the image-based watermark supported options.
type WatermarkImage struct {
Left int
Expand Down Expand Up @@ -214,6 +227,7 @@ type Options struct {
Rotate Angle
Background Color
Gravity Gravity
AddText AddText
Watermark Watermark
WatermarkImage WatermarkImage
Type ImageType
Expand Down
35 changes: 35 additions & 0 deletions resizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ func resizer(buf []byte, o Options) ([]byte, error) {
return nil, err
}

// Add text, if necessary
image, err = AddTextToImage(image, o.AddText)
if err != nil {
return nil, err
}

// Add watermark, if necessary
image, err = watermarkImageWithAnotherImage(image, o.WatermarkImage)
if err != nil {
Expand Down Expand Up @@ -355,6 +361,35 @@ func watermarkImageWithText(image *C.VipsImage, w Watermark) (*C.VipsImage, erro
return image, nil
}

func AddTextToImage(image *C.VipsImage, a AddText) (*C.VipsImage, error) {
if a.Text == "" {
return image, nil
}

// Defaults
if a.Font == "" {
a.Font = WatermarkFont
}
if a.Width == 0 {
a.Width = int(math.Floor(float64(image.Xsize / 6)))
}
if a.DPI == 0 {
a.DPI = 150
}
if a.Opacity == 0 {
a.Opacity = 0.25
} else if a.Opacity > 1 {
a.Opacity = 1
}

image, err := vipsAddText(image, a)
if err != nil {
return nil, err
}

return image, nil
}

func watermarkImageWithAnotherImage(image *C.VipsImage, w WatermarkImage) (*C.VipsImage, error) {

if len(w.Buf) == 0 {
Expand Down
31 changes: 31 additions & 0 deletions vips.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ type vipsWatermarkOptions struct {
Background [3]C.double
}

type vipsAddTextOptions struct {
Width C.int
Height C.int
DPI C.int
Top C.int
Left C.int
Background [3]C.double
Opacity C.float
}

type vipsWatermarkImageOptions struct {
Left C.int
Top C.int
Expand Down Expand Up @@ -292,6 +302,27 @@ func vipsWatermark(image *C.VipsImage, w Watermark) (*C.VipsImage, error) {
return out, nil
}

func vipsAddText(image *C.VipsImage, w AddText) (*C.VipsImage, error) {
var out *C.VipsImage

text := C.CString(w.Text)
font := C.CString(w.Font)
background := [3]C.double{C.double(w.Background.R), C.double(w.Background.G), C.double(w.Background.B)}

textOpts := vipsWatermarkTextOptions{text, font}
opts := vipsAddTextOptions{C.int(w.Width), C.int(w.Height), C.int(w.DPI), C.int(w.Top), C.int(w.Left), background, C.float(w.Opacity)}

defer C.free(unsafe.Pointer(text))
defer C.free(unsafe.Pointer(font))

err := C.vips_add_text(image, &out, (*C.WatermarkTextOptions)(unsafe.Pointer(&textOpts)), (*C.AddTextOptions)(unsafe.Pointer(&opts)))
if err != 0 {
return nil, catchVipsError()
}

return out, nil
}

func vipsRead(buf []byte) (*C.VipsImage, ImageType, error) {
var image *C.VipsImage
imageType := vipsImageType(buf)
Expand Down
55 changes: 55 additions & 0 deletions vips.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ typedef struct {
double Background[3];
} WatermarkOptions;

typedef struct {
int Width;
int Height;
int DPI;
int Top;
int Left;
double Background[3];
float Opacity;
} AddTextOptions;

typedef struct {
int Left;
int Top;
Expand Down Expand Up @@ -451,6 +461,51 @@ vips_watermark(VipsImage *in, VipsImage **out, WatermarkTextOptions *to, Waterma
return 0;
}

int
vips_add_text(VipsImage *in, VipsImage **out, WatermarkTextOptions *to, AddTextOptions *o) {
double ones[3] = { 1, 1, 1 };

VipsImage *base = vips_image_new();
VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 10);
t[0] = in;

// Make the mask.
if (
vips_text(&t[1], to->Text,
"width", o->Width,
"dpi", o->DPI,
"font", to->Font,
NULL) ||
vips_linear1(t[1], &t[2], o->Opacity, 0.0, NULL) ||
vips_cast(t[2], &t[3], VIPS_FORMAT_UCHAR, NULL) ||
vips_embed(t[3], &t[4], o->Left, o->Top, o->Width, o->Height, NULL)
) {
g_object_unref(base);
return 1;
}

// Make the constant image to paint the text with.
if (
vips_black(&t[5], 1, 1, NULL) ||
vips_linear(t[5], &t[6], ones, o->Background, 3, NULL) ||
vips_cast(t[6], &t[7], VIPS_FORMAT_UCHAR, NULL) ||
vips_copy(t[7], &t[8], "interpretation", t[0]->Type, NULL) ||
vips_embed(t[8], &t[9], 0, 0, t[0]->Xsize, t[0]->Ysize, "extend", VIPS_EXTEND_COPY, NULL)
) {
g_object_unref(base);
return 1;
}

// Blend the mask and text and write to output.
if (vips_ifthenelse(t[4], t[9], t[0], out, "blend", TRUE, NULL)) {
g_object_unref(base);
return 1;
}

g_object_unref(base);
return 0;
}

int
vips_gaussblur_bridge(VipsImage *in, VipsImage **out, double sigma, double min_ampl) {
#if (VIPS_MAJOR_VERSION == 7 && VIPS_MINOR_VERSION < 41)
Expand Down

0 comments on commit 4031199

Please sign in to comment.