Example #1
0
// Compose the spritesheet image and return an array of individual sprite data.
func GenerateSpriteSheet(images []Image, log *golog.Logger) (sheet draw.Image, sprites []Sprite) {
	var (
		sheetHeight int = 0
		sheetWidth  int = 0
	)
	sprites = make([]Sprite, 0)

	// calculate the size of the spritesheet and accumulate position and padding
	// data for the individual sprites within the sheet
	for _, img := range images {
		bounds := img.Bounds()

		_, name, factor := IsMagnified(img.Name)
		thisTopPadding := math.Ceil(factor)
		thisBottomPadding := thisTopPadding

		// see if the image dimensions are a multiple of the magnification factor
		fudge := 0
		if math.Mod(float64(bounds.Dy()), factor) != 0 {
			fudge = 1
			log.Warningf("Height of sprite `%s` (%dpx) is not a multiple of its magnification factor (%vx); rounding up to avoid truncated pixels.", name, bounds.Dy(), factor)
		}

		var prevBottomPadding float64
		if len(sprites) == 0 {
			prevBottomPadding = 0
			thisTopPadding = 0
		} else {
			prevBottomPadding = float64(sprites[len(sprites)-1].BottomPadding)
		}

		thisTopPadding = math.Max(thisTopPadding, prevBottomPadding)
		thisTopPaddingInt := int(thisTopPadding)
		thisBottomPaddingInt := int(thisBottomPadding)
		newSprite := Sprite{
			name,
			factor,
			thisTopPaddingInt,
			thisBottomPaddingInt,
			image.Rect(0, sheetHeight+thisTopPaddingInt-fudge, bounds.Dx(), sheetHeight+thisTopPaddingInt+bounds.Dy()),
		}
		sprites = append(sprites, newSprite)
		sheetHeight += bounds.Dy() + thisTopPaddingInt
		if bounds.Dx() > sheetWidth {
			sheetWidth = bounds.Dx()
		}
	}

	// create the sheet image
	sheet = image.NewRGBA(image.Rect(0, 0, sheetWidth, sheetHeight))

	// compose the sheet
	for i, img := range images {
		draw.Draw(sheet, sprites[i].Rectangle, img, image.Pt(0, 0), draw.Src)
	}

	return sheet, sprites
}
Example #2
0
// Write a stylesheet string to a file.
func WriteStyleSheet(style string, folder string, baseName string, log *golog.Logger) (err error) {
	dir, err := os.Open(folder)
	if err != nil {
		err = os.MkdirAll(folder, 0775)
		if err != nil {
			log.Errorf("Unable to create stylesheet output folder '%s'", folder)
			return err
		} else {
			log.Infof("Created stylesheet output folder '%s'", folder)
		}
	} else {
		defer dir.Close()
	}

	fullname := filepath.Join(folder, baseName)
	os.Remove(fullname)
	outFile, err := os.Create(fullname)
	if err != nil {
		log.Errorf("Couldn't create SCSS output file '%s'", fullname)
		return err
	} else {
		defer outFile.Close()
	}
	_, err = outFile.WriteString(style)
	if err != nil {
		log.Errorf("Problem writing styles to '%s'", fullname)
		return err
	}
	return nil
}
Example #3
0
// Write the spritesheet image to a file.
func WriteSpriteSheet(img image.Image, folder string, name string, log *golog.Logger) (err error) {
	dir, err := os.Open(folder)
	if err != nil {
		err = os.MkdirAll(folder, 0775)
		if err != nil {
			log.Errorf("Unable to create sprite-sheet output folder '%s'", folder)
			return err
		} else {
			log.Infof("Created sprite-sheet output folder '%s'", folder)
		}
	} else {
		defer dir.Close()
	}

	fullname := filepath.Join(folder, name+".png")
	os.Remove(fullname)
	outFile, err := os.Create(fullname)
	if err != nil {
		log.Errorf("Couldn't create sprite-sheet output file '%s'", fullname)
		return err
	} else {
		defer outFile.Close()
	}
	err = png.Encode(outFile, img)
	if err != nil {
		log.Warningf("Problem writing sprite-sheet to '%s'", fullname)
		return err
	}
	return nil
}
Example #4
0
// Take a folder name, read all the images within, and return them in an array.
func ReadImageFolder(path string, log *golog.Logger) (images []Image, err error) {
	dir, err := os.Open(path)
	if err != nil {
		log.Errorf("Couldn't open folder '%s'", path)
		return nil, err
	} else {
		defer dir.Close()
	}
	dirInfo, err := dir.Stat()
	if err != nil {
		log.Errorf("Couldn't gather information for folder '%s'", path)
		return nil, err
	}
	if !dirInfo.IsDir() {
		// just ignore the request if it isn't a folder
		return nil, nil
	}

	names, err := dir.Readdirnames(0)
	if err != nil {
		log.Errorf("Problem gathering names of image files in '%s'", path)
		return nil, err
	}

	// put these in a canonical order, otherwise it'll be platform-specific
	sort.Strings(names)

	images = make([]Image, 0)
	for _, name := range names {
		fullName := filepath.Join(path, name)
		imgFile, err := os.Open(fullName)
		if err != nil {
			log.Errorf("Couldn't open image file '%s'", fullName)
			continue
		} else {
			defer imgFile.Close()
		}
		imgInfo, err := imgFile.Stat()
		if err != nil {
			log.Errorf("Couldn't gather information for image file '%s'", fullName)
		}
		if imgInfo.IsDir() {
			// skip non-files
			continue
		}
		ext := strings.ToLower(filepath.Ext(name))
		if ext == ".png" {
			img, err := png.Decode(imgFile)
			if err != nil {
				log.Errorf("Problem decoding png image in '%s'", fullName)
				continue
			}
			name = name[0 : len(name)-len(ext)]
			images = append(images, Image{name, img})
		} else if ext == ".jpg" || ext == ".jpeg" {
			img, err := jpeg.Decode(imgFile)
			if err != nil {
				log.Errorf("Problem decoding jpeg image in '%s'", fullName)
				continue
			}
			name = name[0 : len(name)-len(ext)]
			images = append(images, Image{name, img})
		} else {
			log.Debugf("Ignoring unrecognized file '%s'", fullName)
		}
	}

	if len(images) == 0 {
		log.Warningf("Folder '%s' contains no images; no sprite-sheet will be generated", path)
	}

	return images, nil
}
Example #5
0
// Read a folder containing subfolders which contain sprites, and generate a
// spritesheet for each subfolder.
func GenerateSpriteSheetsFromFolders(superFolder, outputFolder, outputURI string, generateScss, checkTimeStamps bool, log *golog.Logger) (spriteSheets []Image, styleSheets []string, err error) {

	container, err := os.Open(superFolder)
	if err != nil && os.IsNotExist(err) {
		log.Infof("Specified sprite folder '%s' does not exist; not generating anything", superFolder)
		return nil, nil, err
	} else if err != nil {
		log.Errorf("Couldn't open folder '%s' containing sprite subfolders", superFolder)
		return nil, nil, err
	} else {
		defer container.Close()
	}
	containerInfo, err := container.Stat()
	if err != nil {
		log.Errorf("Couldn't gather information for '%s'", superFolder)
		return nil, nil, err
	}
	if !containerInfo.IsDir() {
		log.Errorf("'%s' must be a folder", superFolder)
		return nil, nil, err
	}
	folders, err := container.Readdirnames(0)
	if err != nil {
		log.Errorf("Problem gathering names of sprite subfolders in '%s'", superFolder)
		return nil, nil, err
	}

	spriteSheets = make([]Image, 0)
	styleSheets = make([]string, 0)

	var anyErrors error = nil
	for _, folder := range folders {
		if checkTimeStamps {
			modified, err := SpritesModified(filepath.Join(superFolder, folder), filepath.Join(outputFolder, folder+".png"))
			if err != nil {
				log.Errorf("problem checking timestamps of '%s' and '%s.png'", folder, folder)
				anyErrors = err
				continue
			}
			if !modified {
				log.Infof("no sprites have been added or modified since '%s.png' was generated; skipping generation", folder)
				continue
			}
		}
		images, err := ReadImageFolder(filepath.Join(superFolder, folder), log)
		if err != nil {
			anyErrors = err
			continue
		} else if len(images) > 0 {
			sheet, sprites := GenerateSpriteSheet(images, log)
			vars := GenerateScssVariables(outputURI, folder, sheet, sprites)
			mixins := GenerateScssMixins(outputURI, folder, sheet, sprites)
			classes := GenerateCssClasses(outputURI, folder, sheet, sprites)
			spriteSheets = append(spriteSheets, Image{folder, sheet})
			if generateScss {
				styleSheets = append(styleSheets, fmt.Sprintf("%s\n%s\n%s", vars, mixins, classes))
			} else {
				styleSheets = append(styleSheets, classes+"\n")
			}
		}
		// else {
		// 	log.Warning("'%s' contains no images; no sprite-sheet generated", filepath.Join(superFolder, folder))
		// }
	}
	err = anyErrors

	return
}
Example #6
0
// Read a single folder containing sprites, and generate a single spritesheet
func GenerateSpriteSheetFromFolder(inputFolder, outputFolder, outputURI string, generateScss, checkTimeStamps bool, log *golog.Logger) (spriteSheet Image, styleSheet string, skipped bool, err error) {
	sheetName := filepath.Base(inputFolder)

	folder, ferr := os.Open(inputFolder)
	err = ferr
	if err != nil && os.IsNotExist(err) {
		log.Infof("Specified sprite folder '%s' does not exist; not generating anything", inputFolder)
		return
	} else if err != nil {
		log.Errorf("Couldn't open folder '%s' containing sprite images", inputFolder)
		return
	} else {
		defer folder.Close()
	}
	folderInfo, ferr := folder.Stat()
	err = ferr
	if err != nil {
		log.Errorf("Couldn't gather information for '%s'", inputFolder)
		return
	}
	if !folderInfo.IsDir() {
		log.Errorf("'%s' must be a folder", inputFolder)
		return
	}

	if checkTimeStamps {
		modified, ferr := SpritesModified(inputFolder, filepath.Join(outputFolder, sheetName+".png"))
		err = ferr
		if err != nil {
			log.Errorf("problem checking timestamps of '%s' and '%s.png'", inputFolder, sheetName)
			return
		}
		if !modified {
			log.Infof("no sprites have been added or modified since '%s.png' was generated; skipping generation", sheetName)
			skipped = true
			return
		}
	}

	images, ferr := ReadImageFolder(inputFolder, log)
	err = ferr
	if err != nil {
		log.Errorf("Problem reading sprite images in '%s'", inputFolder)
		return
	} else if len(images) > 0 {
		sheet, sprites := GenerateSpriteSheet(images, log)
		spriteSheet = Image{sheetName, sheet}
		vars := GenerateScssVariables(outputURI, sheetName, spriteSheet, sprites)
		mixins := GenerateScssMixins(outputURI, sheetName, spriteSheet, sprites)
		classes := GenerateCssClasses(outputURI, sheetName, spriteSheet, sprites)
		if generateScss {
			styleSheet = fmt.Sprintf("%s\n%s\n%s", vars, mixins, classes)
		} else {
			styleSheet = classes + "\n"
		}
		return
	} else {
		skipped = true
		return
	}
	return
}