feat(#1): initial implementation

master
Tomas Aparicio 11 years ago
parent 8bcefc7736
commit 63f4b01c8d

@ -0,0 +1,12 @@
root = true
[*]
indent_style = tabs
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

3
.gitignore vendored

@ -0,0 +1,3 @@
/bimg
/bundle
bin

@ -0,0 +1,7 @@
language: go
go:
- 1.4
- 1.3
- 1.2
- release
- tip

@ -0,0 +1,24 @@
The MIT License
Copyright (c) Tomas Aparicio and contributors
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,5 @@
package bimg
import . "github.com/tj/go-debug"
var debug = Debug("bimg")

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

@ -1,3 +1,12 @@
package bimg
type Image struct {}
/*
#cgo pkg-config: vips
#include "vips/vips.h"
*/
import "C"
type Image struct {
buf []byte
image *C.struct__VipsImage
}

@ -0,0 +1,29 @@
package bimg
const QUALITY = 80
type Gravity int
type Interpolator int
var interpolations = map[Interpolator]string{
BICUBIC: "bicubic",
BILINEAR: "bilinear",
NOHALO: "nohalo",
}
func (i Interpolator) String() string {
return interpolations[i]
}
type Options struct {
Height int
Width int
Crop bool
Enlarge bool
Extend int
Embed bool
Interpolator Interpolator
Gravity Gravity
Quality int
}

@ -0,0 +1,136 @@
package bimg
/*
#cgo pkg-config: vips
#include "vips/vips.h"
*/
import "C"
import (
"math"
)
const (
BICUBIC Interpolator = iota
BILINEAR
NOHALO
)
func Resize(buf []byte, o Options) ([]byte, error) {
// detect (if possible) the file type
defer C.vips_thread_shutdown()
image, err := vipsRead(buf)
if err != nil {
return nil, err
}
//var tmpImage *C.struct__VipsImage
/*
// feed it
imageLength := C.size_t(len(buf))
imageBuf := unsafe.Pointer(&buf[0])
debug("buffer: %s", buf[0])
C.vips_jpegload_buffer_seq(imageBuf, imageLength, &image)
*/
// defaults
if o.Quality == 0 {
o.Quality = QUALITY
}
// get WxH
inWidth := int(image.Xsize)
inHeight := int(image.Ysize)
// crop
if o.Crop {
left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity)
o.Width = int(math.Min(float64(inWidth), float64(o.Width)))
o.Height = int(math.Min(float64(inHeight), float64(o.Height)))
image, err = vipsExtract(image, left, top, o.Width, o.Height)
if err != nil {
return nil, err
}
//err := C.vips_extract_area_0(image, &tmpImage, C.int(left), C.int(top), C.int(o.Width), C.int(o.Height))
//C.g_object_unref(C.gpointer(image))
//image = tmpImage
}
// rotate
r := Rotation{180}
image, err = Rotate(image, r)
if err != nil {
return nil, err
}
// Finally save
//var ptr unsafe.Pointer
//length := C.size_t(0)
//C.vips_jpegsave_custom(image, &ptr, &length, 1, C.int(o.Quality), 0)
//C.g_object_unref(C.gpointer(image))
//C.g_object_unref(C.gpointer(newImage))
// get back the buffer
//buf = C.GoBytes(ptr, C.int(length))
// cleanup
//C.g_free(C.gpointer(ptr))
buf, err = vipsSave(image, vipsSaveOptions{Quality: o.Quality})
if err != nil {
return nil, err
}
C.vips_error_clear()
return buf, nil
}
type Rotation struct {
angle int
}
func (a Rotation) calculate() int {
angle := a.angle
divisor := angle % 90
if divisor != 0 {
angle = a.angle - divisor
}
return angle
}
func Rotate(image *C.struct__VipsImage, r Rotation) (*C.struct__VipsImage, error) {
//vips := &Vips{}
return vipsRotate(image, r.calculate())
}
const (
CENTRE Gravity = iota
NORTH
EAST
SOUTH
WEST
)
func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
left, top := 0, 0
switch gravity {
case NORTH:
left = (inWidth - outWidth + 1) / 2
case EAST:
left = inWidth - outWidth
top = (inHeight - outHeight + 1) / 2
case SOUTH:
left = (inWidth - outWidth + 1) / 2
top = inHeight - outHeight
case WEST:
top = (inHeight - outHeight + 1) / 2
default:
left = (inWidth - outWidth + 1) / 2
top = (inHeight - outHeight + 1) / 2
}
return left, top
}

@ -0,0 +1,38 @@
package bimg
import (
"io/ioutil"
"net/http"
"os"
"testing"
)
func TestResize(t *testing.T) {
options := Options{Width: 800, Height: 600, Crop: false}
img, err := os.Open("fixtures/space.jpg")
if err != nil {
t.Fatal(err)
}
defer img.Close()
buf, err := ioutil.ReadAll(img)
if err != nil {
t.Fatal(err)
}
newImg, err := Resize(buf, options)
if err != nil {
t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
}
debug("Image %s", http.DetectContentType(newImg))
if http.DetectContentType(newImg) != "image/jpeg" {
t.Fatal("Image is not jpeg")
}
err = ioutil.WriteFile("fixtures/test.jpg", newImg, 0644)
if err != nil {
t.Fatal("Cannot save the image")
}
}

@ -0,0 +1,10 @@
package bimg
type Type struct {
Name string
Mime string
}
func DetermineType(buf []byte) *Type {
return &Type{Name: "jpg", Mime: "image/jpg"}
}

@ -0,0 +1,100 @@
package bimg
/*
#cgo pkg-config: vips
#include "vips.h"
*/
import "C"
import (
"errors"
"runtime"
"unsafe"
)
func init() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
err := C.vips_init(C.CString("bimg"))
if err != 0 {
C.vips_shutdown()
panic("unable to start vips!")
}
C.vips_concurrency_set(1) // default
C.vips_cache_set_max_mem(100 * 1024 * 1024) // 100 MB
C.vips_cache_set_max(500) // 500 operations
}
type Vips struct {
buf []byte
}
func vipsRotate(image *C.struct__VipsImage, degrees int) (*C.struct__VipsImage, error) {
var out *C.struct__VipsImage
err := C.vips_rotate(image, &out, C.int(degrees))
C.g_object_unref(C.gpointer(image))
if err != 0 {
return nil, vipsError()
}
defer C.g_object_unref(C.gpointer(out))
return out, nil
}
func vipsRead(buf []byte) (*C.struct__VipsImage, error) {
var image *C.struct__VipsImage
// feed it
length := C.size_t(len(buf))
imageBuf := unsafe.Pointer(&buf[0])
err := C.vips_jpegload_buffer_seq(imageBuf, length, &image)
if err != 0 {
return nil, vipsError()
}
return image, nil
}
func vipsExtract(image *C.struct__VipsImage, left int, top int, width int, height int) (*C.struct__VipsImage, error) {
var buf *C.struct__VipsImage
err := C.vips_extract_area_0(image, &buf, C.int(left), C.int(top), C.int(width), C.int(height))
C.g_object_unref(C.gpointer(image))
if err != 0 {
return nil, vipsError()
}
return buf, nil
}
type vipsSaveOptions struct {
Quality int
}
func vipsSave(image *C.struct__VipsImage, o vipsSaveOptions) ([]byte, error) {
var ptr unsafe.Pointer
length := C.size_t(0)
err := C.vips_jpegsave_custom(image, &ptr, &length, 1, C.int(o.Quality), 0)
if err != 0 {
return nil, vipsError()
}
C.g_object_unref(C.gpointer(image))
buf := C.GoBytes(ptr, C.int(length))
// cleanup
C.g_free(C.gpointer(ptr))
return buf, nil
}
func vipsError() error {
s := C.GoString(C.vips_error_buffer())
C.vips_error_clear()
C.vips_thread_shutdown()
return errors.New(s)
}

@ -0,0 +1,91 @@
#include <stdlib.h>
#include <vips/vips.h>
#include <vips/vips7compat.h>
int
vips_initialize()
{
return vips_init("bimg");
};
int
vips_affine_interpolator(VipsImage *in, VipsImage **out, double a, double b, double c, double d, VipsInterpolate *interpolator)
{
return vips_affine(in, out, a, b, c, d, "interpolate", interpolator, NULL);
};
int
vips_jpegload_buffer_seq(void *buf, size_t len, VipsImage **out)
{
return vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
};
int
vips_jpegload_buffer_shrink(void *buf, size_t len, VipsImage **out, int shrink)
{
return vips_jpegload_buffer(buf, len, out, "shrink", shrink, NULL);
};
int
vips_webpload_buffer_seq(void *buf, size_t len, VipsImage **out)
{
return vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
};
int
vips_pngload_buffer_seq(void *buf, size_t len, VipsImage **out)
{
return vips_pngload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
};
int
vips_shrink_0(VipsImage *in, VipsImage **out, double xshrink, double yshrink)
{
return vips_shrink(in, out, xshrink, yshrink, NULL);
};
int
vips_copy_0(VipsImage *in, VipsImage **out)
{
return vips_copy(in, out, NULL);
}
int
vips_rotate(VipsImage *in, VipsImage **buf, int angle)
{
int rotate = VIPS_ANGLE_D0;
if (angle == 90) {
rotate = VIPS_ANGLE_D90;
} else if (angle == 180) {
rotate = VIPS_ANGLE_D180;
} else if (angle == 270) {
rotate = VIPS_ANGLE_D270;
}
return vips_rot(in, buf, rotate, NULL);
}
int
vips_embed_extend(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend)
{
return vips_embed(in, out, left, top, width, height, "extend", extend, NULL);
}
int
vips_colourspace_0(VipsImage *in, VipsImage **out, VipsInterpretation space)
{
return vips_colourspace(in, out, space, NULL);
};
int
vips_extract_area_0(VipsImage *in, VipsImage **out, int left, int top, int width, int height)
{
return vips_extract_area(in, out, left, top, width, height, NULL);
}
int
vips_jpegsave_custom(VipsImage *in, void **buf, size_t *len, int strip, int quality, int interlace)
{
return vips_jpegsave_buffer(in, buf, len, "strip", strip, "Q", quality, "optimize_coding", TRUE, "interlace", interlace, NULL);
}
Loading…
Cancel
Save