diff --git a/image.go b/image.go index 4edfa93..c5f183a 100644 --- a/image.go +++ b/image.go @@ -1,86 +1,103 @@ package main -// type imagingTask struct { -// target imagecache.ImageLocation -// in chan imagecache.ImageCache -// out chan *image.RGBA -// } - -// var ( -// imageScaleProcess = make(chan imagingTask, 256) -// ) - -// func imagingWorker(tasks <-chan imagingTask) { -// for t := range tasks { -// if t.target.S < imageCacheStorageLevel { -// from := <-t.in -// t.out <- imageScaleGetWithin(from.img, t.target) -// } else { -// log.Println("Unimplemented scaler > imageCacheStorageLevel") -// } -// } -// } - -// func imagingProcessor(ctx context.Context) { -// var wg sync.WaitGroup - -// sn := cfg.GetDSInt(4, "imaging_workers") -// wg.Add(sn) -// for i := 0; i < sn; i++ { -// go func() { -// imagingWorker(imageScaleProcess) -// wg.Done() -// }() -// } - -// <-ctx.Done() -// log.Println("Image worker shutting down") -// close(imageScaleProcess) - -// wg.Wait() -// log.Println("Image worker shutdown") -// } - -// // gets subsection from image based in icIN -// func imageScaleGetWithin(from *image.RGBA, target imagecache.ImageLocation) *image.RGBA { -// // target image size -// is := powarr16[target.S] - -// // TODO: probably reuse buffers with sync.Pool -// ret := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{is, is}}) - -// // absolute position of the target -// ax, az := target.X*powarr[target.S], target.Z*powarr[target.S] - -// // input relative position of the target -// ix, iz := icIN(ax, az) - -// pt := image.Point{(ix / powarr[target.S]) * is, (iz / powarr[target.S]) * is} -// draw.Draw(ret, ret.Rect, from, pt, draw.Over) - -// return ret -// } - -// // stitches multiple images together -// func imageScaleGetFrom(from <-chan imageTask, target imagecache.ImageLocation) *image.RGBA { -// // TODO: probably reuse buffers with sync.Pool -// is := powarr16[target.S] - -// ret := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{is, is}}) - -// // TODO: scale down images -// return ret -// } - -// func imageGetSync(loc imagecache.ImageLocation, ignoreCache bool, doDrawing bool) (*image.RGBA, error) { -// if !ignoreCache { -// i := imageCacheGetBlockingLoc(loc) -// if i != nil { -// return i, nil -// } -// } -// if doDrawing { - -// } -// return nil, nil -// } +import ( + "image" + "image/draw" + "log" + "runtime/debug" + + "github.com/maxsupermanhd/WebChunk/chunkStorage" + imagecache "github.com/maxsupermanhd/WebChunk/imageCache" + "github.com/nfnt/resize" +) + +func imageGetSync(loc imagecache.ImageLocation, ignoreCache bool) (*image.RGBA, error) { + if !ignoreCache { + i := imageCacheGetBlockingLoc(loc) + if i != nil { + return i, nil + } + } + img, err := renderTile(loc) + if err != nil { + return img, err + } + if img != nil { + imageCacheSaveLoc(img, loc) + } + return img, err +} + +func renderTile(loc imagecache.ImageLocation) (*image.RGBA, error) { + + f := findTTypeProviderFunc(loc) + if f == nil { + log.Printf("Image variant %q was not found", loc.Variant) + return nil, nil + } + ff := *f + + _, s, err := chunkStorage.GetWorldStorage(storages, loc.World) + if err != nil { + return nil, nil + } + getter, painter := ff(s) + + scale := 1 + if loc.S > 0 { + scale = int(2 << (loc.S - 1)) // because math.Pow is very slow (43.48 vs 0.1881 ns/op) + } + + imagesize := scale * 16 + if imagesize > 512 { + imagesize = 512 + } + + img := image.NewRGBA(image.Rect(0, 0, int(imagesize), int(imagesize))) + imagescale := int(imagesize / scale) + offsetx := loc.X * scale + offsety := loc.Z * scale + cc, err := getter(loc.World, loc.Dimension, loc.X*scale, loc.Z*scale, loc.X*scale+scale, loc.Z*scale+scale) + if err != nil { + return nil, err + } + if len(cc) == 0 { + return nil, nil + } + for _, c := range cc { + // TODO: break on cancel + placex := int(c.X - offsetx) + placey := int(c.Z - offsety) + var chunk *image.RGBA + chunk = func(d interface{}) *image.RGBA { + defer func() { + if err := recover(); err != nil { + log.Println(loc.X, loc.Z, err) // TODO: pass error outwards + debug.PrintStack() + } + chunk = nil + }() + var ret *image.RGBA + ret = nil + ret = painter(d) + return ret + }(c.Data) + if chunk == nil { + continue + } + tile := resize.Resize(uint(imagescale), uint(imagescale), chunk, resize.NearestNeighbor) + draw.Draw(img, image.Rect(placex*int(imagescale), placey*int(imagescale), placex*int(imagescale)+imagescale, placey*int(imagescale)+imagescale), + tile, image.Pt(0, 0), draw.Over) + } + return img, nil +} + +func findTTypeProviderFunc(loc imagecache.ImageLocation) *ttypeProviderFunc { + for tt := range ttypes { + if tt.Name == loc.Variant { + f := ttypes[tt] + return &f // TODO: fix this ugly thing + } + } + return nil +} diff --git a/ws.go b/ws.go index 74007c0..28227fa 100644 --- a/ws.go +++ b/ws.go @@ -113,7 +113,20 @@ func wsClientHandler(w http.ResponseWriter, r *http.Request, ctx context.Context subbedTiles := map[imagecache.ImageLocation]bool{} asyncTileRequestor := func(loc imagecache.ImageLocation) { - ret := marshalBinaryTileUpdate(loc, imageCacheGetBlockingLoc(loc)) + img, err := imageGetSync(loc, false) + if err != nil { + b, _ := json.Marshal(map[string]any{ + "Action": "message", + "Data": fmt.Sprintf("Error rendering tile %s: %s", loc.String(), err), + }) + wQ <- wsmessage{ + msgType: websocket.TextMessage, + msgData: b, + } + return + } + // TODO: fix time of check time of use + ret := marshalBinaryTileUpdate(loc, img) if !wQdidClose.Load() { wQ <- wsmessage{ msgType: websocket.BinaryMessage,