func (hs *AutoSlicer) getSlice(r Region) image.Image { var ( bounds image.Rectangle img draw.Image ) bounds = r.Sub(r.Min) bounds.Max = bounds.Max.Sub(image.Pt(1, 1)) img = image.NewRGBA(bounds) draw.Draw(img, bounds, hs.img, r.Min.Add(image.Pt(1, 1)), draw.Src) return img }
// Returns a sub-image of the input image. The bounding rectangle is the // smallest possible rectangle that includes all pixels that have alpha > 0, // with one pixel of border on all sides. func minimalSubImage(src image.Image) *subImage { bounds := src.Bounds() var new_bounds image.Rectangle new_bounds.Max = bounds.Min new_bounds.Min = bounds.Max for x := bounds.Min.X; x <= bounds.Max.X; x++ { for y := bounds.Min.Y; y <= bounds.Max.Y; y++ { c := src.At(x, y) _, _, _, a := c.RGBA() if a > 0 { if x < new_bounds.Min.X { new_bounds.Min.X = x } if y < new_bounds.Min.Y { new_bounds.Min.Y = y } if x > new_bounds.Max.X { new_bounds.Max.X = x } if y > new_bounds.Max.Y { new_bounds.Max.Y = y } } } } // // We want one row/col of boundary between characters so that we don't get // // annoying artifacts new_bounds.Min.X-- new_bounds.Min.Y-- new_bounds.Max.X++ new_bounds.Max.Y++ if new_bounds.Min.X > new_bounds.Max.X || new_bounds.Min.Y > new_bounds.Max.Y { new_bounds = image.Rect(0, 0, 0, 0) } return &subImage{src, new_bounds} }
// Add finds a position for the given image in the atlas and copies the image // there. It returns the new sub-image. If there is no more space in the atlas, // it returns an error. func (a *Atlas) Add(id string, img image.Image) (SubImage, error) { // determine the position of the new image rect, err := a.packer.Insert(img.Bounds().Dx(), img.Bounds().Dy()) if err != nil { return SubImage{}, errors.New("unable to add image to atlas: " + err.Error()) } var bounds image.Rectangle bounds.Min = a.Image.Bounds().Min.Add(image.Pt(rect.X, rect.Y)) bounds.Max = bounds.Min.Add(image.Pt(rect.Width, rect.Height)) // copy the image data into the atlas draw.Draw(a.Image, bounds, img, img.Bounds().Min, draw.Src) sub := SubImage{ Image: subImage{ a.Image, bounds, }, ID: id, } a.SubImages = append(a.SubImages, sub) return sub, nil }
func nextLine(r *image.Rectangle, xstart int) { r.Min.X = xstart r.Min.Y += 6 r.Max = r.Min.Add(image.Point{4, 6}) }
// Returns a blocking channel of Step. // // Once Step.Done() is true, the calculation has finished and the channel is closed. // You can use this to abort calculating larger image resamples or to show percentage // done indicators. // // The filter F is the resampling function used. See the provided samplers for examples. // Additionally X- and YWrap functions are used to define how image boundaries are // treated. See the provided Clamp function for examples. func ResizeToChannelWithFilter(dst image.Image, dstRect image.Rectangle, src image.Image, srcRect image.Rectangle, F Filter, XWrap, YWrap WrapFunc) (<-chan Step, chan<- bool, error) { if src == nil { return nil, nil, ErrSourceImageIsInvalid } if F.Apply == nil || F.Support <= 0 { return nil, nil, ErrMissingFilter } if XWrap == nil || YWrap == nil { return nil, nil, ErrMissingWrapFunc } var dstBounds image.Rectangle if dst == nil { dstBounds.Max = srcRect.Size() } else { dstBounds = dst.Bounds() } newSize := dstRect.Size() if newSize.X < 0 || newSize.Y < 0 { return nil, nil, ErrTargetSizeIsInvalid } resultChannel := make(chan Step) doneChannel := make(chan bool) // Code for the KeepAlive closure used to // break the calulculation into blocks. // Sends on the channel only happen every opIncrement // operations. For now this is hardcoded to a reasonable value. var opCount, totalOps, lastOps, opIncrement int opIncrement = 200 * 1000 keepAlive := func(ops int) bool { opCount += ops if opCount >= lastOps { select { case <-doneChannel: return false case resultChannel <- step{image: nil, total: totalOps, done: opCount}: lastOps += opIncrement return true } } return true } sendImage := func(img image.Image) { select { case resultChannel <- step{image: img, total: totalOps, done: opCount}: case <-doneChannel: } } if newSize.X == 0 || newSize.Y == 0 { go sendImage(image.NewNRGBA64(image.Rect(0, 0, newSize.X, newSize.Y))) return resultChannel, doneChannel, nil } go func() { // Send first empty step before we do any real work. keepAlive(0) xFilter, xOps := makeDiscreteFilter(F, XWrap, dstRect.Dx(), srcRect.Dx()) yFilter, yOps := makeDiscreteFilter(F, YWrap, dstRect.Dy(), srcRect.Dy()) if dst == nil { dst = image.NewNRGBA64(dstRect) } dst := dst.(*image.NRGBA64) xy_ops := yOps*srcRect.Dx() + xOps*dstRect.Dy() yx_ops := xOps*srcRect.Dy() + yOps*dstRect.Dx() if xy_ops < yx_ops { totalOps = xy_ops tmpBounds := image.Rect(0, 0, srcRect.Dx(), dstRect.Dy()) var tmp *image.NRGBA64 if tmpBounds.Dx() < dstRect.Dx() { tmp = image.NewNRGBA64(tmpBounds) } else { tmp = dst } resampleAxisNRGBA64(yAxis, keepAlive, tmp, tmpBounds, src, srcRect, yFilter) resampleAxisNRGBA64(xAxis, keepAlive, dst, dstRect, tmp, tmpBounds, xFilter) } else { totalOps = yx_ops tmpBounds := image.Rect(0, 0, dstRect.Dx(), srcRect.Dy()) var tmp *image.NRGBA64 if tmpBounds.Dy() < dstRect.Dy() { tmp = image.NewNRGBA64(tmpBounds) } else { tmp = dst } resampleAxisNRGBA64(xAxis, keepAlive, tmp, tmpBounds, src, srcRect, xFilter) resampleAxisNRGBA64(yAxis, keepAlive, dst, dstRect, tmp, tmpBounds, yFilter) } //log.Printf("Resize %v -> %v %d kOps (xy =%d,yx =%d)",src.Bounds().Max, newSize,opCount/1000, xy_ops/1000, yx_ops/1000) sendImage(dst) }() return resultChannel, doneChannel, nil }
func main() { title, file, out := os.Args[1], os.Args[2], os.Args[3] f, err := os.Open(file) if err != nil { log.Fatal(err) } defer f.Close() m := readHeader(f) log.Printf("%+v", m.Header) var emitPage func(page int, img image.Image, jpgData []byte) if strings.HasSuffix(out, ".pdf") { log.Printf("output to PDF file %s", out) w, err := os.Create(out) if err != nil { log.Fatal(err) } p, err := NewPDFWriter(w) if err != nil { log.Fatal(err) } p.WriteInfo(title, time.Now()) emitPage = func(page int, img image.Image, jpgData []byte) { off := p.offset p.WriteJPEGPage(img, jpgData) log.Printf("added page %d (wrote %d bytes)", page, p.offset-off) } finish := func() { p.Flush() if p.err != nil { log.Fatal(p.err) } err = w.Close() if err != nil { log.Fatal(err) } log.Printf("PDF file complete: %d bytes", p.offset) } defer finish() } else { log.Printf("output to JPEG files %s/*.jpg", out) os.MkdirAll(out, 0755) emitPage = func(page int, img image.Image, jpgData []byte) { outpath := filepath.Join(out, fmt.Sprintf("page%04d.jpg", page)) err := ioutil.WriteFile(outpath, jpgData, 0644) if err != nil { log.Fatal(err) } log.Printf("written image to %s", outpath) } } var buf []byte readJpeg := func(n int) (image.Image, error) { if len(buf) < n { buf = make([]byte, n) } _, err := io.ReadFull(f, buf[:n]) if err != nil { return nil, err } return jpeg.Decode(bytes.NewReader(buf)) } var bufsize image.Rectangle bufsize.Max.X = int(m.Width) bufsize.Max.Y = int(m.Height) imgBuf := image.NewRGBA(bufsize) jpgBuf := new(bytes.Buffer) for page, tsizes := range m.ImgSizes { // log.Printf("reading page %d", page+1) // read tiles: they are arranged in columns for i, tsize := range tsizes[:m.NX*m.NY] { x, y := i/int(m.NY), i%int(m.NY) off, _ := f.Seek(0, os.SEEK_CUR) img, err := readJpeg(int(tsize)) if err != nil { log.Printf("cannot read tile %d(%d,%d) at 0x%x: %s", page+1, x, y, off, err) } else { var dest image.Rectangle dest.Min.X = 256 * (i / int(m.NY)) dest.Min.Y = 256 * (i % int(m.NY)) dest.Max = bufsize.Max draw.Src.Draw(imgBuf, dest, img, img.Bounds().Min) } } // skip full page image off, _ := f.Seek(0, os.SEEK_CUR) _, err := readJpeg(int(tsizes[len(tsizes)-2])) if err != nil { log.Printf("cannot read page view %d at 0x%x: %s", page+1, off, err) } jpgBuf.Reset() err = jpeg.Encode(jpgBuf, imgBuf, &jpeg.Options{Quality: 85}) if err != nil { log.Fatal(err) } emitPage(page+1, imgBuf, jpgBuf.Bytes()) // clear buffer for i := range imgBuf.Pix { imgBuf.Pix[i] = 0 } } for page, tsizes := range m.ImgSizes { // skip thumbnail. off, _ := f.Seek(0, os.SEEK_CUR) _, err := readJpeg(int(tsizes[len(tsizes)-1])) if err != nil { log.Printf("thumbnail of page %d (offset 0x%x) is corrupted: %s", page+1, off, err) } } off, _ := f.Seek(0, os.SEEK_CUR) stat, _ := f.Stat() if off < stat.Size() { log.Printf("WARNING: trailing bytes: %d", stat.Size()-off) } }
func (f *Frame) Insert(r []rune, p0 uint64) { if p0 > uint64(f.nchars) || len(r) == 0 || f.Background == nil { return } var rect image.Rectangle var col, tcol *draw.Image pts := make([]points, 0) n0 := f.findbox(0, 0, p0) cn0 := p0 nn0 := n0 pt0 := f.ptofcharnb(p0, n0) ppt0 := pt0 opt0 := pt0 pt1 := f.bxscan(r, &ppt0) ppt1 := pt1 b := f.box[n0] if n0 < f.nbox { f.cklinewrap(&pt0, b) f.cklinewrap0(&ppt1, b) } f.modified = true /* * ppt0 and ppt1 are start and end of insertion as they will appear when * insertion is complete. pt0 is current location of insertion position * (p0); pt1 is terminal point (without line wrap) of insertion. */ if f.p0 == f.p1 { f.Tick(f.Ptofchar(f.p0), false) } /* * Find point where old and new x's line up * Invariants: * pt0 is where the next box (b, n0) is now * pt1 is where it will be after the insertion * If pt1 goes off the rectangle, we can toss everything from there on */ npts := 0 for ; pt1.X != pt0.X && pt1.Y != f.Rect.Max.Y && n0 < f.nbox; npts++ { b := f.box[n0] f.cklinewrap(&pt0, b) f.cklinewrap0(&pt1, b) if b.Nrune > 0 { n, fits := f.canfit(pt1, b) if !fits { panic("frame.canfit == 0") } if n != b.Nrune { f.splitbox(uint64(n0), uint64(n)) b = f.box[n0] } } if npts == nalloc { pts = append(pts, make([]points, npts+DELTA)...) nalloc += DELTA b = f.box[n0] } pts[npts].pt0 = pt0 pts[npts].pt1 = pt1 if pt1.Y == f.Rect.Max.Y { break } f.advance(&pt0, b) pt1.X += f.newwid(pt1, b) cn0 += uint64(nrune(b)) n0++ } if pt1.Y > f.Rect.Max.Y { panic("frame.Insert pt1 too far") } if pt1.Y == f.Rect.Max.Y && n0 < f.nbox { f.nchars -= f.strlen(n0) f.delbox(n0, f.nbox-1) } if n0 == f.nbox { div := f.Font.Height if pt1.X > f.Rect.Min.X { div++ } f.nlines = (pt1.Y - f.Rect.Min.Y) / div } else if pt1.Y != pt0.Y { y := f.Rect.Max.Y q0 := pt0.Y + f.Font.Height q1 := pt1.Y + f.Font.Height f.nlines += (q1 - q0) / f.Font.Height if f.nlines > f.maxlines { f.chop(ppt1, p0, nn0) } if pt1.Y < y { rect = f.Rect rect.Min.Y = q1 rect.Max.Y = y if q1 < y { f.Background.Draw(rect, f.Background, nil, image.Pt(f.Rect.Min.X, q0)) } rect.Min = pt1 rect.Max.X = pt1.X + (f.Rect.Max.X - pt0.X) rect.Max.Y += q1 f.Background.Draw(rect, f.Background, nil, pt0) } } /* * Move the old stuff down to make room. The loop will move the stuff * between the insertion and the point where the x's lined up. * The draw()s above moved everything down after the point they lined up. */ y := 0 if pt1.Y == f.Rect.Max.Y { y = pt1.Y } for n0 = n0 - 1; npts >= 0; n0-- { b := f.box[n0] pt := pts[npts].pt1 if b.Nrune > 0 { rect.Min = pt rect.Max = rect.Min rect.Max.X += b.Wid rect.Max.Y += f.Font.Height f.Background.Draw(rect, f.Background, nil, pts[npts].pt0) /* clear bit hanging off right */ if npts == 0 && pt.Y > pt0.Y { rect.Min = opt0 rect.Max = opt0 rect.Max.X = f.Rect.Max.X rect.Max.Y += f.Font.Height if f.p0 <= cn0 && cn0 < f.p1 { /* b+1 is inside selection */ col = f.Cols[colHigh] } else { col = f.Cols[colBack] } f.Background.Draw(rect, col, nil, rect.Min) } else if pt.Y < y { rect.Min = pt rect.Max = pt rect.Min.X += b.Wid rect.Max.X = f.Rect.Max.X rect.Max.Y += f.Font.Height if f.p0 <= cn0 && cn0 < f.p1 { col = f.Cols[colHigh] } else { col = f.Cols[colBack] } f.Background.Draw(rect, col, nil, rect.Min) } y = pt.Y cn0 -= uint64(b.Nrune) } else { rect.Min = pt rect.Max = pt rect.Max.X += b.Wid rect.Max.Y += f.Font.Height if rect.Max.X >= f.Rect.Max.X { rect.Max.X = f.Rect.Max.X } cn0-- if f.p0 <= cn0 && cn0 < f.p1 { col = f.Cols[colHigh] tcol = f.Cols[colHText] } else { col = f.Cols[colBack] tcol = f.Cols[colText] } f.Background.Draw(rect, col, nil, rect.Min) y = 0 if pt.X == f.Rect.Min.X { y = pt.Y } } } if f.p0 < p0 && p0 <= f.p1 { col = f.Cols[colHigh] tcol = f.Cols[colHText] } else { col = f.Cols[colBack] tcol = f.Cols[colText] } f.SelectPaint(ppt0, ppt1, col) f.DrawText(ppt0, tcol, col) f.addbox(uint64(nn0), uint64(frame.nbox)) for n := 0; n < frame.nbox; n++ { f.box[nn0+n] = frame.box[n] } if nn0 > 0 && f.box[nn0-1].Nrune >= 0 && ppt0.X-f.box[nn0-1].Wid >= f.Rect.Min.X { nn0-- ppt0.X -= f.box[nn0].Wid } n0 += frame.nbox if n0 < f.nbox-1 { n0++ } f.clean(ppt0, nn0, n0) f.nchars += frame.nchars if f.p0 >= p0 { f.p0 += uint64(frame.nchars) } if f.p0 >= uint64(f.nchars) { f.p0 = uint64(f.nchars) } if f.p1 >= p0 { f.p1 += uint64(frame.nchars) } if f.p1 >= uint64(f.nchars) { f.p1 += uint64(f.nchars) } if f.p0 == f.p1 { f.Tick(f.Ptofchar(f.p0), true) } }