Beispiel #1
0
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
}
Beispiel #2
0
// 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}
}
Beispiel #3
0
// 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
}
Beispiel #4
0
func nextLine(r *image.Rectangle, xstart int) {
	r.Min.X = xstart
	r.Min.Y += 6
	r.Max = r.Min.Add(image.Point{4, 6})
}
Beispiel #5
0
// 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
}
Beispiel #6
0
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)
	}
}
Beispiel #7
0
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)
	}
}