diff --git a/fixtures/test_icc_prophoto.jpg b/fixtures/test_icc_prophoto.jpg new file mode 100644 index 0000000..ebf7f02 Binary files /dev/null and b/fixtures/test_icc_prophoto.jpg differ diff --git a/metadata_test.go b/metadata_test.go index ceb2b2e..663ec56 100644 --- a/metadata_test.go +++ b/metadata_test.go @@ -39,6 +39,7 @@ func TestMetadata(t *testing.T) { space string }{ {"test.jpg", "jpeg", 0, false, false, "srgb"}, + {"test_icc_prophoto.jpg", "jpeg", 0, false, true, "srgb"}, {"test.png", "png", 0, true, false, "srgb"}, {"test.webp", "webp", 0, false, false, "srgb"}, } diff --git a/resize.go b/resize.go index 7b2cffc..77bfbfc 100644 --- a/resize.go +++ b/resize.go @@ -403,11 +403,11 @@ func imageCalculations(o *Options, inWidth, inHeight int) float64 { // Fixed width, auto height case o.Width > 0: factor = xfactor - o.Height = int(math.Floor(float64(inHeight) / factor)) + o.Height = roundFloat(float64(inHeight) / factor) // Fixed height, auto width case o.Height > 0: factor = yfactor - o.Width = int(math.Floor(float64(inWidth) / factor)) + o.Width = roundFloat(float64(inWidth) / factor) // Identity transform default: o.Width = inWidth @@ -418,6 +418,14 @@ func imageCalculations(o *Options, inWidth, inHeight int) float64 { return factor } +func roundFloat(f float64) int { + if f < 0 { + return int(math.Ceil(f - 0.5)) + } else { + return int(math.Floor(f + 0.5)) + } +} + func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) { left, top := 0, 0 diff --git a/resize_test.go b/resize_test.go index 96a3a2f..aeffa67 100644 --- a/resize_test.go +++ b/resize_test.go @@ -1,6 +1,9 @@ package bimg import ( + "bytes" + "image" + "image/jpeg" "io/ioutil" "os" "path" @@ -112,6 +115,24 @@ func TestResizeCustomSizes(t *testing.T) { } } +func TestResizePrecision(t *testing.T) { + // see https://github.com/h2non/bimg/issues/99 + img := image.NewGray16(image.Rect(0, 0, 1920, 1080)) + input := &bytes.Buffer{} + jpeg.Encode(input, img, nil) + + opts := Options{Width: 300} + newImg, err := Resize(input.Bytes(), opts) + if err != nil { + t.Fatalf("Resize(imgData, %#v) error: %#v", opts, err) + } + + size, _ := Size(newImg) + if size.Width != opts.Width { + t.Fatalf("Invalid width: %d", size.Width) + } +} + func TestRotate(t *testing.T) { options := Options{Width: 800, Height: 600, Rotate: 270, Crop: true} buf, _ := Read("fixtures/test.jpg") diff --git a/vips.h b/vips.h index dfdfa0f..b5b3bb2 100644 --- a/vips.h +++ b/vips.h @@ -50,7 +50,7 @@ typedef struct { double Background[3]; } WatermarkOptions; -static int +static unsigned long has_profile_embed(VipsImage *image) { return vips_image_get_typeof(image, VIPS_META_ICC_NAME); } @@ -247,11 +247,21 @@ vips_webpsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int qual ); } +int +vips_is_16bit (VipsInterpretation interpretation) { + return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16; +} + int vips_flatten_background_brigde(VipsImage *in, VipsImage **out, double background[3]) { + background[0] *= 256; + background[1] *= 256; + background[2] *= 256; + VipsArrayDouble *vipsBackground = vips_array_double_new(background, 3); return vips_flatten(in, out, "background", vipsBackground, + "max_alpha", vips_is_16bit(in->Type) ? 65535.0 : 255.0, NULL ); }