func writeImagePng(filename string, pixels []float32, xres, yres int) { outImage := image.NewNRGBA(image.Rect(0, 0, xres, yres)) to_byte := func(v float32) uint8 { // apply gamma and convert to 0..255 return uint8(Clamp(255.0*math.Pow(float64(v), 1.0/2.2), 0.0, 255.0)) } for y := 0; y < yres; y++ { for x := 0; x < xres; x++ { var fcolor color.NRGBA fcolor.R = to_byte(pixels[3*(y*xres+x)+0]) fcolor.G = to_byte(pixels[3*(y*xres+x)+1]) fcolor.B = to_byte(pixels[3*(y*xres+x)+2]) fcolor.A = 0xff outImage.Set(x, y, fcolor) } } f, err := os.Create(filename) defer f.Close() if err != nil { Error("Error writing PNG \"%s\"", filename) } else { png.Encode(f, outImage) } }
// Palette returns Palette if any. func (cfg *Config) Palette() color.Palette { if cfg.ColorMode != ColorModeIndexed { return nil } // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_38034 // 0x0417(1046) | (Photoshop 6.0) Transparency Index. // 2 bytes for the index of transparent color, if any. transparentColorIndex := -1 if r, ok := cfg.Res[0x0417]; ok { transparentColorIndex = int(readUint16(r.Data, 0)) } pal := make(color.Palette, len(cfg.ColorModeData)/3) for i := range pal { c := color.NRGBA{ cfg.ColorModeData[i], cfg.ColorModeData[i+256], cfg.ColorModeData[i+512], 0xFF, } if i == transparentColorIndex { c.A = 0 } pal[i] = c } return pal }
// render one row of pixels by calculating a colour for each pixel. // The image pixel row number is r. Fill the pixel colour into the // image after the colour has been calculated. func (r row) render(rt *rtrace, a, b, c lin.V3, img *image.NRGBA, seed *uint32) { rgba := color.NRGBA{0, 0, 0, 255} t, v1, v2 := lin.NewV3(), lin.NewV3(), lin.NewV3() // temp vectors. colour, orig, dir := lin.NewV3(), lin.NewV3(), lin.NewV3() for x := (rt.iw - 1); x >= 0; x-- { colour.SetS(13, 13, 13) // Use a very dark default colour. // Cast 64 rays per pixel for blur (stochastic sampling) and soft-shadows. for cnt := 0; cnt < 64; cnt++ { // Add randomness to the camera origin 17,16,8 t.Scale(&a, rnd(seed)-0.5).Scale(t, 99).Add(t, v1.Scale(&b, rnd(seed)-0.5).Scale(v1, 99)) orig.SetS(17, 16, 8).Add(orig, t) // Add randomness to the camera direction. rnda := rnd(seed) + float64(x) rndb := float64(r) + rnd(seed) dir.Scale(t, -1) dir.Add(dir, v1.Scale(&a, rnda).Add(v1, v2.Scale(&b, rndb)).Add(v1, &c).Scale(v1, 16)) dir.Unit() // accumulate the colour from each of the 64 rays. sample := rt.sample(*orig, *dir, seed) colour = sample.Scale(&sample, 3.5).Add(&sample, colour) } // set the final pixel colour in the image. rgba.R = byte(colour.X) // red rgba.G = byte(colour.Y) // green rgba.B = byte(colour.Z) // blue img.SetNRGBA(rt.iw-x, int(r), rgba) } }
func setRandomBrightness(c *color.NRGBA, max uint8) { minc := min3(c.R, c.G, c.B) maxc := max3(c.R, c.G, c.B) if maxc > max { return } n := rand.Intn(int(max-maxc)) - int(minc) c.R = uint8(int(c.R) + n) c.G = uint8(int(c.G) + n) c.B = uint8(int(c.B) + n) }
func TestNewVertical_singleStop(t *testing.T) { c := color.NRGBA{0, 255, 255, 0} v := NewVertical(200, 400, []Stop{{0.0, c}}).At(100, 150) r0, g0, b0, a0 := v.RGBA() r1, g1, b1, a1 := c.RGBA() if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 { t.Fatalf("v.At(%d, %d) = %+v, want %+v", 200, 400, v, c) } }
func TestNewVertical_noStops(t *testing.T) { v := NewVertical(200, 400, []Stop{}).At(100, 150) c := color.NRGBA{255, 0, 255, 255} r0, g0, b0, a0 := v.RGBA() r1, g1, b1, a1 := c.RGBA() if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 { t.Fatalf("v.At(%d, %d) = %+v, want %+v", 200, 400, v, c) } }
func hexToColorNRGBA(hex string) color.NRGBA { c := color.NRGBA{} getIntensity := func(str string) uint8 { if u, err := hexToUint8(str); err == nil { return u } return 0 } if len(hex) >= 6 { c.R = getIntensity(hex[0:2]) c.G = getIntensity(hex[2:4]) c.B = getIntensity(hex[4:6]) } if len(hex) == 8 { c.A = getIntensity(hex[6:8]) } else { c.A = 255 } return c }
func Decode(r io.Reader) (outImage image.Image, err error) { b := make([]byte, 128) if _, err = io.ReadFull(r, b); err != nil { return } else if b[2] != 1 || b[3] != 8 { err = ErrFormat return } w := int(LittleEndian.Uint16(b[8:])) + 1 h := int(LittleEndian.Uint16(b[10:])) + 1 var p []byte if p, err = ioutil.ReadAll(r); len(p) < 769 || p[len(p)-769] != 12 { err = ErrFormat return } b = p p = p[len(p)-768:] palette := make(color.Palette, 0) for i := 0; i < 256; i++ { o := i * 3 color := color.NRGBA{p[o+0], p[o+1], p[o+2], 0xff} if color.R == 0x9f && color.G == 0x5b && color.B == 0x53 { color.A = 0 } palette = append(palette, color) } pix := make([]byte, w*h) i := 0 for y := 0; y < h; y++ { for x := 0; x < w; { data := b[i] i++ if data&0xc0 == 0xc0 { runLen := data & 0x3f data = b[i] i++ for ; runLen > 0; runLen-- { pix[x+y*w] = data x++ } } else { pix[x+y*w] = data x++ } } } outImage = &image.Paletted{ Pix: pix, Stride: w, Rect: image.Rect(0, 0, w, h), Palette: palette, } return }
func drawMotionMap(ske0, ske1 *image.NRGBA, m *MotionMap, shift *ShiftMap) (img *image.RGBA) { bounds := ske0.Bounds().Intersect(ske1.Bounds()).Intersect(m.Bounds) if shift != nil { bounds = bounds.Intersect(shift.Bounds) } s := 10 img = image.NewRGBA(image.Rectangle{bounds.Min.Mul(s), bounds.Max.Mul(s)}) gc := draw2d.NewGraphicContext(img) for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { c := color.NRGBA{255, 255, 255, 255} if ske0.At(x/s, y/s).(color.NRGBA).A > 0 { c.R = 0 } if ske1.At(x/s, y/s).(color.NRGBA).A > 0 { c.G = 0 } img.Set(x, y, c) } } count := 0 for y := m.Bounds.Min.Y; y < m.Bounds.Max.Y; y++ { for x := m.Bounds.Min.X; x < m.Bounds.Max.X; x++ { v := GetMotion(m, x, y) if v.X != 0 || v.Y != 0 { cx, cy := x*s+s/2, y*s+s/2 dx, dy := v.X*s, v.Y*s gc.SetStrokeColor(color.RGBA{0, 0, 0, 255}) gc.MoveTo(float64(cx), float64(cy)) gc.LineTo(float64(cx+dx), float64(cy+dy)) gc.Stroke() count++ } if shift != nil { sv := GetShift(shift, x, y) if sv.Dx != 0.0 || sv.Dy != 0.0 { cx, cy := x*s+s/2, y*s+s/2 sf := float64(s) //fmt.Println(x, y, sv) gc.SetStrokeColor(color.RGBA{0, 200, 0, 255}) gc.MoveTo(float64(cx), float64(cy)) gc.LineTo(float64(cx)+sv.Dx*sf, float64(cy)+sv.Dy*sf) gc.Stroke() } } } } fmt.Println("count:", count) return }
// Compare compares a and b using pdiff algorithm. func (d *perceptual) Compare(a, b image.Image) (image.Image, int, error) { ab, bb := a.Bounds(), b.Bounds() w, h := ab.Dx(), ab.Dy() if w != bb.Dx() || h != bb.Dy() { return nil, -1, ErrSize } diff := image.NewNRGBA(image.Rect(0, 0, w, h)) var ( wg sync.WaitGroup aLAB, bLAB [][]*labColor aLap, bLap [][][]float64 ) wg.Add(2) go func() { aLAB, aLap = labLap(a, d.gamma, d.lum) wg.Done() }() go func() { bLAB, bLap = labLap(b, d.gamma, d.lum) wg.Done() }() cpd := make([]float64, lapLevels) // cycles per degree cpd[0] = 0.5 * float64(w) / d.odp // 0.5 * pixels per degree for i := 1; i < lapLevels; i++ { cpd[i] = 0.5 * cpd[i-1] } csfMax := csf(3.248, 100.0) freq := make([]float64, lapLevels-2) for i := 0; i < lapLevels-2; i++ { freq[i] = csfMax / csf(cpd[i], 100.0) } wg.Wait() var npix int // num of diff pixels for y := 0; y < h; y++ { for x := 0; x < w; x++ { adapt := math.Max(0.5*(aLap[d.ai][y][x]+bLap[d.ai][y][x]), 1e-5) mask := make([]float64, lapLevels-2) contrast := make([]float64, lapLevels-2) var contrastSum float64 for i := 0; i < lapLevels-2; i++ { n1 := math.Abs(aLap[i][y][x] - aLap[i+1][y][x]) n2 := math.Abs(bLap[i][y][x] - bLap[i+1][y][x]) d1 := math.Abs(aLap[i+2][y][x]) d2 := math.Abs(bLap[i+2][y][x]) d := math.Max(d1, d2) contrast[i] = math.Max(n1, n2) / math.Max(d, 1e-5) mask[i] = vmask(contrast[i] * csf(cpd[i], adapt)) contrastSum += contrast[i] } if contrastSum < 1e-5 { contrastSum = 1e-5 } var factor float64 for i := 0; i < lapLevels-2; i++ { factor += contrast[i] * freq[i] * mask[i] / contrastSum } if factor < 1 { factor = 1 } else if factor > 10 { factor = 10 } delta := math.Abs(aLap[0][y][x] - bLap[0][y][x]) pass := true // pure luminance test if delta > factor*tvi(adapt) { pass = false } else if !d.nocolor { // CIE delta E test with modifications cf := d.cf // ramp down the color test in scotopic regions if adapt < 10.0 { // don't do color test at all cf = 0.0 } da := aLAB[y][x].a - bLAB[y][x].a db := aLAB[y][x].b - bLAB[y][x].b if (da*da+db*db)*cf > factor { pass = false } } c := color.NRGBA{0, 0, 0, 0xff} if !pass { npix++ c.R = 0xff //ar, ag, ab, _ := a.At(x, y).RGBA() //br, bg, bb, _ := b.At(x, y).RGBA() //c.R = uint8((math.Abs(float64(ar)-float64(br)) / 0xffff) * 0xff) //c.G = uint8((math.Abs(float64(ag)-float64(bg)) / 0xffff) * 0xff) //c.B = uint8((math.Abs(float64(ab)-float64(bb)) / 0xffff) * 0xff) } diff.Set(x, y, c) } } return diff, npix, nil }
// Decode decodes a Half-Life image. func Decode(r io.Reader) (outImage image.Image, err error) { b := make([]byte, 40) if _, err = io.ReadFull(r, b); err != nil { return } name := string(bytes.ToLower(b[:bytes.IndexByte(b[:16], 0)])) width := int(LittleEndian.Uint32(b[16:])) height := int(LittleEndian.Uint32(b[20:])) dataOff := int(LittleEndian.Uint32(b[24:])) if dataOff == 0 { outImage = &HLTex{ &image.Paletted{ Pix: nil, Stride: width, Rect: image.Rect(0, 0, width, height), Palette: nil, }, name, } return } else if dataOff < len(b) { err = ErrFormat return } var palOff int if palOff = int(LittleEndian.Uint32(b[36:])); palOff != 0 { palOff += width * height / 64 } else if palOff = int(LittleEndian.Uint32(b[32:])); palOff != 0 { palOff += width * height / 16 } else if palOff = int(LittleEndian.Uint32(b[28:])); palOff != 0 { palOff += width * height / 4 } else { palOff = dataOff + width*height } dataOff -= len(b) palOff -= len(b) var size int b = make([]byte, palOff+2+256*3) if size, err = r.Read(b); err != nil { return } else if size < palOff+2 { err = ErrFormat return } palSize := int(LittleEndian.Uint16(b[palOff:])) palOff += 2 if palSize > 256 || size < palOff+palSize { err = ErrFormat return } palette := make(color.Palette, 0) for i := 0; i < palSize; i++ { o := i * 3 color := color.NRGBA{b[palOff+o+0], b[palOff+o+1], b[palOff+o+2], 0xff} if color.R == color.G && color.G == 0 && color.B == 0xff { color.A = 0 } palette = append(palette, color) } for i := palSize; i < 256; i++ { palette = append(palette, color.NRGBA{0, 0, 0, 0}) } outImage = &HLTex{ &image.Paletted{ Pix: b[dataOff : dataOff+width*height], Stride: width, Rect: image.Rect(0, 0, width, height), Palette: palette, }, name, } return }