Пример #1
0
// Определяем — это серое изображение?
func isgray(im *gd.Image) bool {
	for x := im.Sx() - 1; x >= 0; x-- {
		for y := im.Sy() - 1; y >= 0; y-- {
			c := im.ColorsForIndex(im.ColorAt(x, y))

			if c["red"] != c["green"] || c["red"] != c["blue"] {
				return false
			}
		}
	}

	return true
}
Пример #2
0
func main() {
	options := parseoptions()

	// Показываем силу му-у-у-у-у?
	if v, ok := options["moo"]; ok && v == "1" {
		moo()
	}

	if runtime.GOMAXPROCS(-1) > 1 {
		runtime.GOMAXPROCS(3)
	}

	// Преобразование маски файлов в более традиционный для Go формат
	regexp, _ := r.Compile(`(\{[^\}]+\})`)

	oMask := regexp.ReplaceAllStringFunc(options["mask"], func(m string) string {
		return "[" + s.Join(s.Split(m[1:len(m)-1], ","), "") + "]"
	})

	// Первоначальное значение out-dir
	wd, _ := os.Getwd()
	oOutDir := path.Clean(path.Join(wd, options["out-dir"]))

	// Составляем список файлов, по которому будем двигаться
	var oFileList []string

	if options["recursive"] == "1" {
		if path.IsAbs(options["out-dir"]) {
			oOutDir = path.Clean(options["out-dir"])
		}

		oFileList = getrecurlist(oMask, oOutDir)
	} else {
		oFileList, _ = fp.Glob(oMask)
	}

	// Создаём директорий для результата, если он нужен
	if options["keep-name"] == "0" && !fileexists(oOutDir) {
		os.MkdirAll(oOutDir, 0777)
	}

	// Сколько файлов получилось?
	oLen := len(oFileList)

	if oLen < 1 {
		os.Stdout.WriteString("Файлы не найдены\n")
		os.Exit(1)
	}

	// Маска для нового имени
	now := time.Now().Format("2006.01.02")
	oNameMask := path.Join(options["out-dir"], now)
	if oLen > 1 {
		prec := strconv.Itoa(len(strconv.Itoa(oLen)))
		oNameMask += ".%0" + prec + "d.jpg"

		fmt.Printf("Found %d JPEG files.\n", oLen)
	} else {
		oNameMask += ".jpg"
		fmt.Println("Found 1 JPEG file.")
	}

	// Нормализация background, должны получиться три hex числа
	// в Go очень примитивные regexp :(
	if re := r.MustCompile(`^#[0-9a-fA-F]+`); len(options["background"]) == 7 && !re.MatchString(options["background"]) {
		options["background"] = "#ffffff"
	}

	// И переводим background компоненты
	oBgColor := [3]int{}

	for i := 1; i < len(options["background"]); i += 2 {
		c, _ := strconv.ParseInt(options["background"][i:i+2], 16, 0)
		oBgColor[i>>1] = int(c)
	}

	// Уголки для скруглений
	var corner *gd.Image
	defer corner.Destroy()

	coready := make(chan *gd.Image)

	oRadius, _ := strconv.Atoi(options["radius"])
	if oRadius > 0 {
		go func() {
			oRadius, _ := strconv.Atoi(options["radius"])

			// важно, что это локальная переменная, иначе будет
			// баг — указатель будет уже не пустой, а уголки ещё
			// не будут готовы
			corner := gd.CreateTrueColor(oRadius<<1+2, oRadius<<1+2)
			corner.AlphaBlending(false)
			corner.SaveAlpha(true)
			trans := corner.ColorAllocateAlpha(oBgColor[0], oBgColor[1], oBgColor[2], 127)
			back := corner.ColorAllocate(oBgColor[0], oBgColor[1], oBgColor[2])

			corner.Fill(0, 0, trans)
			smoothellipse(corner, oRadius, oRadius+1, oRadius, back)

			// инвертируем прозрачность пикселей
			for x := 0; x < corner.Sx(); x++ {
				for y := 0; y < corner.Sy(); y++ {
					c := corner.ColorsForIndex(corner.ColorAt(x, y))
					c["alpha"] = 127 - c["alpha"]

					nc := corner.ColorAllocateAlpha(c["red"], c["green"], c["blue"], c["alpha"])
					corner.SetPixel(x, y, nc)
				}
			}

			coready <- corner
		}()
	}

	// Качество сохраняемой картинки
	oQuality, _ := strconv.Atoi(options["quality"])

	// проверяем, доступен ли jpegtran
	// пробуем найти jpegtran
	oJtname, oJt := func() (string, bool) {
		if jpegtran != "" {
			if fileexists(jpegtran) {
				return jpegtran, true
			}

			jtname := fp.Base(jpegtran)
			paths := bytes.Split([]byte(os.Getenv("PATH")), []byte{fp.ListSeparator})

			for _, p := range paths {
				if name := fp.Join(string(p), jtname); fileexists(name) {
					return name, true
				}
			}
		}

		return "", false
	}()

	// Временное имя для ч/б профиля
	oProfile := fp.Join(os.TempDir(), "cornet-bolk-bw.txt")
	defer os.Remove(oProfile)

	// Пишем профайл для ч/б изображения, профиль цветного не поддерживается «Оперой»
	profile, e := os.OpenFile(oProfile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0777)
	if e == nil {
		defer profile.Close()
		profile.WriteString("0:   0  0 0 0 ;\n0:   1  8 0 2 ;\n0:   9 63 0 2 ;\n0:   1 63 2 1 ;\n0:   1 63 1 0;")
	}

	oTmpName := fp.Join(os.TempDir(), "cornet-bolk-"+strconv.Itoa(os.Getpid())+"-")
	oSaved := int64(0)

	// Цикл обработки файлов
	for num, name := range oFileList {
		fmt.Printf("Processing %s ... ", name)

		im := gd.CreateFromJpeg(name)
		im.AlphaBlending(true)

		sx := im.Sx()
		sy := im.Sy()
		var w, h int

		// Если указана какая-то разумная ширина, то уменьшим до этой
		// ширины
		if w, _ = strconv.Atoi(options["width"]); w > 0 {
			h = int(float32(sy) * (float32(w) / float32(sx)))
			imresized := gd.CreateTrueColor(w, h)
			im.CopyResampled(imresized, 0, 0, 0, 0, w, h, sx, sy)
			im.Destroy()

			im = imresized
		} else {
			w, h = sx, sy
		}

		if oRadius > 0 {
			if corner == nil {
				corner = <-coready
			}

			if R := oRadius + 1; R > 1 {
				corner.Copy(im, 0, 0, 0, 0, R, R)
				corner.Copy(im, 0, h-R, 0, R, R, R)
				corner.Copy(im, w-R, 0, R, 0, R, R)
				corner.Copy(im, w-R, h-R, R, R, R, R)
			}
		}

		// Если имена не сохраняем, то заменяем на сгенерированное имя
		if options["keep-name"] == "0" {
			if oLen > 1 {
				name = fmt.Sprintf(oNameMask, num+1)
			} else {
				name = oNameMask
			}
		}

		// Jpegtran доступен
		if oJt {
			tmpname := oTmpName + fp.Base(name)
			gray := isgray(im)

			im.Jpeg(tmpname, oQuality)
			im.Destroy()
			im = nil

			// Оптимизация jpeg
			stat, _ := os.Stat(tmpname)
			cmdkeys := []string{"-copy", "none"}

			// Для файлов > 10КБ с вероятностью 94% лучшие результаты даёт progressive
			if stat.Size() > 10*1024 {
				cmdkeys = append(cmdkeys, "-progressive")
			}

			// Если файл серый, то оптимизируем его как серый
			if gray {
				cmdkeys = append(cmdkeys, "-grayscale", "-scans", oProfile)
			}

			cmdkeys = append(cmdkeys, "-optimize", tmpname)
			cmd := exec.Command(oJtname, cmdkeys...)

			buf := make([]byte, 2048)

			fp, _ := os.Create(name)
			out, _ := cmd.StdoutPipe()
			cmd.Start()

			for {
				n, _ := out.Read(buf)

				if n == 0 {
					break
				}

				fp.Write(buf[0:n])
			}

			fp.Close()
			cmd.Wait()

			// TODO: идея такая — stdout замыкаем на Writer, берём с него данные, следим за EXIF
			// не забыть прочитать EXIF из файла

			outstat, _ := os.Stat(name)

			oSaved += stat.Size() - outstat.Size()

			os.Remove(tmpname)
		} else {
			// Jpegtran нам недоступен
			im.Jpeg(name, oQuality)
			im.Destroy()
			im = nil
		}
		fmt.Println("done")
	}

	if oSaved > 0 {
		fmt.Printf("Saved %d bytes after optimization.\n", oSaved)
	}
}
Пример #3
0
// функция грубой отрисовки части дуги окружности
// по алгоритму Ulrich Mierendorf (imageSmoothArc_optimized)
func smootharc(p *gd.Image, cx, cy, a, b float64, fillColor gd.Color, start, stop, seg float64) {
	color := p.ColorsForIndex(fillColor)
	var xp, yp, xa, ya float64

	switch seg {
	case 0:
		xp, yp, xa, ya = 1, -1, 1, -1
	case 1:
		xp, yp, xa, ya = -1, -1, 0, -1
	case 2:
		xp, yp, xa, ya = -1, 1, 0, 0
	case 3:
		xp, yp, xa, ya = 1, 1, 1, 0
	}

	for x := float64(0); x <= a; x++ {
		y := b * math.Sqrt(1-(x*x)/(a*a))
		error := y - float64(int(y))
		y = float64(int(y))

		alpha := int(127 - float64(127-color["alpha"])*error)
		diffColor := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha)

		xx := int(cx + xp*x + xa)

		p.SetPixel(xx, int(cy+yp*(y+1)+ya), diffColor)
		p.Line(xx, int(cy+yp*y+ya), xx, int(cy+ya), fillColor)
	}

	for y := float64(0); y < b; y++ {
		x := a * math.Sqrt(1-(y*y)/(b*b))
		error := x - float64(int(x))
		x = float64(int(x))

		alpha := int(127 - float64(127-color["alpha"])*error)
		diffColor := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha)
		p.SetPixel(int(cx+xp*(x+1)+xa), int(cy+yp*y+ya), diffColor)
	}
}