diff --git a/activity.go b/activity.go index 168a4ca..07582e6 100644 --- a/activity.go +++ b/activity.go @@ -23,7 +23,6 @@ import ( "encoding/json" "fmt" "html" - "image" "io" "log" "net/http" @@ -33,6 +32,8 @@ import ( "strings" "sync" "time" + + "humungus.tedunangst.com/r/webs/image" ) func NewJunk() map[string]interface{} { @@ -222,13 +223,13 @@ func savedonk(url string, name, media string) *Donk { data := buf.Bytes() if strings.HasPrefix(media, "image") { - img, format, err := image.Decode(&buf) + img, err := image.Vacuum(&buf) if err != nil { log.Printf("unable to decode image: %s", err) return nil } - data, format, err = vacuumwrap(img, format) - media = "image/" + format + data = img.Data + media = "image/" + img.Format } res, err := stmtSaveFile.Exec(xid, name, url, media, data) if err != nil { diff --git a/go.mod b/go.mod index e94750c..097a8e6 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,5 @@ require ( golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 humungus.tedunangst.com/r/go-sqlite3 v1.1.2 - humungus.tedunangst.com/r/webs v0.2.0 + humungus.tedunangst.com/r/webs v0.3.0 ) diff --git a/go.sum b/go.sum index 45889d4..fc3f66a 100644 --- a/go.sum +++ b/go.sum @@ -12,5 +12,5 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= humungus.tedunangst.com/r/go-sqlite3 v1.1.2 h1:bRAXNRZ4VNFRFhhG4tdudK4Lv4ktHQAHEppKlDANUFg= humungus.tedunangst.com/r/go-sqlite3 v1.1.2/go.mod h1:FtEEmQM7U2Ey1TuEEOyY1BmphTZnmiEjPsNLEAkpf/M= -humungus.tedunangst.com/r/webs v0.2.0 h1:K0PJK4ZFNB+B0vGbs6huY5br35Z9xR4T6XpcFHmwXgQ= -humungus.tedunangst.com/r/webs v0.2.0/go.mod h1:6yLLDXBaE4pKURa/3/bxoQPod37uAqc/Kq8J0IopWW0= +humungus.tedunangst.com/r/webs v0.3.0 h1:Igx471E1Rabh5BIkVJw9N48xGNEHENx05r/7lOBJRUs= +humungus.tedunangst.com/r/webs v0.3.0/go.mod h1:6yLLDXBaE4pKURa/3/bxoQPod37uAqc/Kq8J0IopWW0= diff --git a/honk.go b/honk.go index 47dc110..4c5ebe5 100644 --- a/honk.go +++ b/honk.go @@ -23,7 +23,6 @@ import ( "fmt" "html" "html/template" - "image" "io" "log" notrand "math/rand" @@ -36,6 +35,7 @@ import ( "time" "github.com/gorilla/mux" + "humungus.tedunangst.com/r/webs/image" "humungus.tedunangst.com/r/webs/login" "humungus.tedunangst.com/r/webs/templates" ) @@ -883,13 +883,10 @@ func savehonk(w http.ResponseWriter, r *http.Request) { data := buf.Bytes() xid := xfiltrate() var media, name string - img, format, err := image.Decode(&buf) + img, err := image.Vacuum(&buf) if err == nil { - data, format, err = vacuumwrap(img, format) - if err != nil { - log.Printf("can't vacuum image: %s", err) - return - } + data = img.Data + format := img.Format media = "image/" + format if format == "jpeg" { format = "jpg" diff --git a/image.go b/image.go deleted file mode 100644 index 170477c..0000000 --- a/image.go +++ /dev/null @@ -1,148 +0,0 @@ -// -// Copyright (c) 2019 Ted Unangst -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -package main - -import ( - "bytes" - "fmt" - "image" - "image/jpeg" - "image/png" - "math" -) - -func lineate(s uint8) float64 { - x := float64(s) - x /= 255.0 - if x < 0.04045 { - x /= 12.92 - } else { - x += 0.055 - x /= 1.055 - x = math.Pow(x, 2.4) - } - return x -} - -func delineate(x float64) uint8 { - if x > 0.0031308 { - x = math.Pow(x, 1/2.4) - x *= 1.055 - x -= 0.055 - } else { - x *= 12.92 - } - x *= 255.0 - return uint8(x) -} - -func blend(d []byte, s1, s2, s3, s4 int) byte { - l1 := lineate(d[s1]) - l2 := lineate(d[s2]) - l3 := lineate(d[s3]) - l4 := lineate(d[s4]) - return delineate((l1 + l2 + l3 + l4) / 4.0) -} - -func squish(d []byte, s1, s2, s3, s4 int) byte { - return uint8((uint32(d[s1]) + uint32(d[s2]) + uint32(d[s3]) + uint32(d[s4])) / 4) -} - -func vacuumwrap(img image.Image, format string) ([]byte, string, error) { - maxdimension := 2048 - for img.Bounds().Max.X > maxdimension || img.Bounds().Max.Y > maxdimension { - switch oldimg := img.(type) { - case *image.NRGBA: - w, h := oldimg.Rect.Max.X/2, oldimg.Rect.Max.Y/2 - newimg := image.NewNRGBA(image.Rectangle{Max: image.Point{X: w, Y: h}}) - for j := 0; j < h; j++ { - for i := 0; i < w; i++ { - p := newimg.Stride*j + i*4 - q1 := oldimg.Stride*(j*2+0) + i*4*2 - q2 := oldimg.Stride*(j*2+1) + i*4*2 - newimg.Pix[p+0] = blend(oldimg.Pix, q1+0, q1+4, q2+0, q2+4) - newimg.Pix[p+1] = blend(oldimg.Pix, q1+1, q1+5, q2+1, q2+5) - newimg.Pix[p+2] = blend(oldimg.Pix, q1+2, q1+6, q2+2, q2+6) - newimg.Pix[p+3] = squish(oldimg.Pix, q1+3, q1+7, q2+3, q2+7) - } - } - img = newimg - case *image.YCbCr: - w, h := oldimg.Rect.Max.X/2, oldimg.Rect.Max.Y/2 - newimg := image.NewYCbCr(image.Rectangle{Max: image.Point{X: w, Y: h}}, - oldimg.SubsampleRatio) - for j := 0; j < h; j++ { - for i := 0; i < w; i++ { - p := newimg.YStride*j + i - q1 := oldimg.YStride*(j*2+0) + i*2 - q2 := oldimg.YStride*(j*2+1) + i*2 - newimg.Y[p+0] = blend(oldimg.Y, q1+0, q1+1, q2+0, q2+1) - } - } - switch newimg.SubsampleRatio { - case image.YCbCrSubsampleRatio444: - w, h = w, h - case image.YCbCrSubsampleRatio422: - w, h = w/2, h - case image.YCbCrSubsampleRatio420: - w, h = w/2, h/2 - case image.YCbCrSubsampleRatio440: - w, h = w, h/2 - case image.YCbCrSubsampleRatio411: - w, h = w/4, h - case image.YCbCrSubsampleRatio410: - w, h = w/4, h/2 - } - for j := 0; j < h; j++ { - for i := 0; i < w; i++ { - p := newimg.CStride*j + i - q1 := oldimg.CStride*(j*2+0) + i*2 - q2 := oldimg.CStride*(j*2+1) + i*2 - newimg.Cb[p+0] = blend(oldimg.Cb, q1+0, q1+1, q2+0, q2+1) - newimg.Cr[p+0] = blend(oldimg.Cr, q1+0, q1+1, q2+0, q2+1) - } - } - img = newimg - default: - return nil, "", fmt.Errorf("can't support image format") - } - } - maxsize := 512 * 1024 - quality := 80 - var buf bytes.Buffer - for { - switch format { - case "png": - png.Encode(&buf, img) - case "jpeg": - jpeg.Encode(&buf, img, &jpeg.Options{Quality: quality}) - default: - return nil, "", fmt.Errorf("can't encode format: %s", format) - } - if buf.Len() > maxsize && quality > 30 { - switch format { - case "png": - format = "jpeg" - case "jpeg": - quality -= 10 - } - buf.Reset() - continue - } - break - } - return buf.Bytes(), format, nil -}