Картинки 2.0

Краен срок
19.11.2013 17:00

Срокът за предаване на решения е отминал

За това предизвикателство ще поработим върху нашия код за обработка на изображения. За целта, в структурата (header) ще се наложи да добавим допълнително поле "Encoding". Вашата задача ще бъде да прочетете данните въз основа на encoding-а, да ги нормализирате и да предоставяте достъп до тях както правехте до сега.

Byte array

В списъка с байтове ще се съхраняват данните за всеки пиксел от изображението.

Encoding

За нашите изображения encoding алгоритъмът ще бъде RLE (Run-length encoding).

RLE е най-простият енкодинг за изображения. Неговата идея произлиза от това, че много изображения съдържат голям брой съседни пиксели с еднакви стойности. За да оптимизира този случай RLE решава да напише брой повторения и стойността (но стойността се записва само веднъж)

Червено, Червено, Червено, Червено, Червено, Зелено, Червено Ще бъде превърнато в: 5 Червено, Зелено, Червено

За повече информация погледнете тук.

Относно нашите данни представянето се променя така: Три сини пиксела RGB:

blue := []byte{3, 0, 0, 255}

Три червени пиксела RGBA: red := []byte{3, 255, 0, 0, 255}

Header

Структурата Header, която ще носи данните за подаденото изображение, ще има данни като:

  • Format пази формата: всички премутации на RGB и на RGBA
  • LineWidth съдържа броя колони от пиксели
  • Encoding string, който пази енкодинг типа. Валидни стойности: "RLE", "None".

Colors

Относно стуктурата на цветовете: В byte масива цветовете ще ви бъдат подадени като число от 0 - 255. За да подобрим поведението на нашето решение към цветовете ще закръгляме всяка стойност, която получим към най-близкия цвят. За останалите, които не искат да ползват междинно представяне, знайте (((color * alpha) + 128)/255), но за тази цел трябва да си дефинирате операции върху цветовете.

Precomputing

Независимо дали изображението е енкоднато, крайният резултат трябва да е с precomputed alpha.

Грешки

След като сме решили да направим решението си по "приемливо", се налага да се погирижим и за "неадекватните" хора, които ползват нашия код. За тази цел ще се наложи InspectPixel и ParseImage да ни връщат грешки. Това налага да добавим в нашата дефиниция на двете функции error към return аргументите. Относно как работят грешките в Go, може да видите тук. За нашата задача няма да ни интересува каква е върнатата грешка. От вас се иска да се погрижите при "странни" случай да бъде върната грешка и данната да е nil.

>>> data := []byte{}
[]
>>> header := Header{"RGB", 3, "None"}
>>> if image, err := ParseImage(data, header); err != nil {
>>>     do stuff
>>> }
>>> if pixel, err := image.InspectPixel(5, 0); err != nil {
>>>     do stuff
>>> }
>>> fmt.Println(pixel.Color())
Red: 31, Green: 33, Blue: 41

Описание

Задачата е да разширим обхвата от данни, които може вашата функция да приема, и да добавим Image Encoding към уравнението. Стуктура Image продължава да има метод InspectPixel. При извикване на InspectPixel, той трябва да връща тип *Pixel, който да има Color, съдържащ Red, Green и Blue като стойности от 0 - 255.

Пример

Пример за извикване без енкодинг.

>>> data := []byte{0, 12, 244, 13, 26, 52, 31, 33, 41}
[0 12 244 13 26 52 31 33 41]
>>> header := Header{"RGB", 3, "None"}
>>> pixel, _ := ParseImage(data, header).InspectPixel(0, 0)
>>> fmt.Println(pixel.Color())
Red: 0, Green: 12, Blue: 244

Пример за извикване с енкодинг.

>>> data := []byte{1, 0, 12, 244, 3, 13, 26, 52, 3, 31, 33, 41}
[2 0 12 244 3 13 26 52 3 31 33 41]
>>> header := Header{"RGB", 3, "RLE"}
>>> pixel, _ := ParseImage(data, header).InspectPixel(0, 0)
>>> fmt.Println(pixel.Color())
Red: 0, Green: 12, Blue: 244
>>> pixel = ParseImage(data, header).InspectPixel(5, 0)
>>> fmt.Println(pixel.Color())
Red: 31, Green: 33, Blue: 41

Решения

Явор Папазов
  • Непроверено
  • 0 успешни тест(а)
  • 6 неуспешни тест(а)
Явор Папазов
package main
import (
"fmt"
"sort"
"strings"
)
const no_encoding string = "None"
const run_length_encoding string = "RLE"
type ParseImageError struct {
err string
}
func (e ParseImageError) Error() string {
return e.err
}
type InspectPixelError struct {
err string
}
func (e InspectPixelError) Error() string {
return e.err
}
type Pixel struct {
Red uint8
Green uint8
Blue uint8
}
func (pixel Pixel) String() string {
return fmt.Sprintf("Red: %d, Green: %d, Blue: %d", pixel.Red, pixel.Green, pixel.Blue)
}
func (pixel Pixel) Color() Pixel {
return pixel
}
type PixelData struct {
width uint64
height uint64
format string
hasAlpha bool
redIndex uint8
greenIndex uint8
blueIndex uint8
alphaIndex uint8
pixelLength uint8
data []byte
hasEncoding bool
lookupTable []uint64
lookupSize uint64
realSize uint64
}
func (pixelData PixelData) InspectPixel(i uint64, j uint64) (result Pixel, err error) {
realIndex := (j*pixelData.width + i)
if realIndex >= pixelData.realSize {
err = InspectPixelError{"Indices out of bounds for image"}
return
}
var realData []byte
if pixelData.hasEncoding {
indexInLookup := uint64(sort.Search(int(pixelData.lookupSize),
func(i int) bool { return realIndex < pixelData.lookupTable[i] })) - 1
indexInLookup *= uint64(pixelData.pixelLength)
realData = pixelData.data[indexInLookup : indexInLookup+uint64(pixelData.pixelLength)]
} else {
start := realIndex * uint64(pixelData.pixelLength)
end := realIndex*uint64(pixelData.pixelLength) + uint64(pixelData.pixelLength)
realData = pixelData.data[start:end]
}
redVal := realData[pixelData.redIndex]
greenVal := realData[pixelData.greenIndex]
blueVal := realData[pixelData.blueIndex]
if !pixelData.hasAlpha {
result = Pixel{redVal, greenVal, blueVal}
return
}
alphaFactor := float64(realData[pixelData.alphaIndex]) / float64(255)
redVal = byte(alphaFactor * float64(redVal))
greenVal = byte(alphaFactor * float64(greenVal))
blueVal = byte(alphaFactor * float64(blueVal))
result = Pixel{redVal, greenVal, blueVal}
return
}
type Header struct {
Format string
LineWidth uint64
Encoding string
}
func ParseImage(pixels []byte, header Header) (result PixelData, err error) {
format := header.Format
pixelLength := uint8(len(format))
width := header.LineWidth
encoding := header.Encoding
var height uint64
alphaIndex := strings.IndexRune(format, 'A')
hasAlpha := (alphaIndex != -1)
redIndex := strings.IndexRune(format, 'R')
if redIndex == -1 {
err = ParseImageError{"Incorrect header format string: no 'red'"}
return
}
greenIndex := strings.IndexRune(format, 'G')
if redIndex == -1 {
err = ParseImageError{"Incorrect header format string: no 'green'"}
return
}
blueIndex := strings.IndexRune(format, 'B')
if blueIndex == -1 {
err = ParseImageError{"Incorrect header format string: no 'blue'"}
return
}
if (uint64(pixelLength)*uint64(pixelLength+1))/2 !=
uint64(redIndex+greenIndex+blueIndex+alphaIndex+4) {
err = ParseImageError{"Incorrect header format string"}
return
}
//fmt.Printf("%d %d\n", width, height)
switch encoding {
case no_encoding:
factor := width * uint64(pixelLength)
length := uint64(len(pixels))
if length%factor != 0 {
err = ParseImageError{"Wrong size of pixels array"}
return
}
height = length / factor
result = PixelData{width, height, format, hasAlpha, uint8(redIndex),
uint8(greenIndex), uint8(blueIndex), uint8(alphaIndex),
pixelLength, pixels, false, nil, 0, width * height}
return
case run_length_encoding:
pixelLength++
greenIndex++
blueIndex++
redIndex++
if alphaIndex != -1 {
alphaIndex++
}
if uint64(len(pixels))%uint64(pixelLength) != 0 {
err = ParseImageError{"Wrong size of pixels array"}
}
listLen := uint64(len(pixels)) / uint64(pixelLength)
lookup := make([]uint64, listLen)
var (
prevValue uint64 = 0
j uint64
)
for i, _ := range lookup {
j = uint64(pixelLength) * uint64(i)
lookup[i] = prevValue
prevValue += uint64(pixels[j])
}
totalLen := prevValue
if totalLen%uint64(width) != 0 {
err = ParseImageError{"Wrong size of pixels array"}
}
height = totalLen / uint64(width)
result = PixelData{width, height, format, hasAlpha, uint8(redIndex),
uint8(greenIndex), uint8(blueIndex), uint8(alphaIndex),
pixelLength, pixels, true, lookup, listLen, width * height}
return
default:
err = ParseImageError{"Incorrect encoding specified"}
return
}
}
/*func main() {
data := []byte{3, 0, 12, 244, 4, 13, 26, 52, 5, 31, 33, 41}
header := Header{"RGB", 3, "RLE"}
pixelData, err := ParseImage(data, header)
if err != nil {
panic(err.Error())
}
fmt.Println(pixelData)
realPixel, err := pixelData.InspectPixel(2, 0)
if err != nil {
panic(err.Error())
}
fmt.Println(realPixel)
}*/
Мартин Ангелов
  • Непроверено
  • 0 успешни тест(а)
  • 6 неуспешни тест(а)
Мартин Ангелов
package main
import (
"errors"
"strings"
)
type Header struct {
Format string
LineWidth uint
Encoding string
}
type Pixel struct {
Red, Green, Blue byte
}
func (p *Pixel) Color() Pixel {
return *p
}
type Image struct {
pixels []Pixel
lineWidth uint
}
func InArray(needle string, haystack []string) bool {
for _, value := range haystack {
if value == needle {
return true
}
}
return false
}
func (i *Image) InspectPixel(x uint, y uint) (Pixel, error) {
if x+y*i.lineWidth > uint(len(i.pixels)) {
return Pixel{}, errors.New("Invalid x and/or y")
}
return i.pixels[x+y*i.lineWidth], nil
}
func ParseImage(data []byte, header Header) (*Image, error) {
allowedFormats := []string{"RGB", "RGBA", "BGRA"}
if !InArray(header.Format, allowedFormats) {
return new(Image), errors.New("Invalid x and/or y")
}
order := strings.Split(header.Format, "")
if header.Encoding == "RLE" {
order = append([]string{"L"}, order...) // Prepend L for "Length"
}
if (uint(len(data)) % (header.LineWidth * uint(len(order)))) != 0 {
return new(Image), errors.New("Invalid Line Width")
}
pixels := []Pixel{}
for i := 0; i < len(data)/len(order); i++ {
pixel := new(Pixel)
repeat := byte(1)
for j, value := range order {
curByte := data[i*len(order)+j]
switch value {
case "R":
pixel.Red = curByte
case "G":
pixel.Green = curByte
case "B":
pixel.Blue = curByte
case "A":
pixel.Red =
byte((int(pixel.Red) * int(curByte)) / 255)
pixel.Green =
byte((int(pixel.Green) * int(curByte)) / 255)
pixel.Blue =
byte((int(pixel.Blue) * int(curByte)) / 255)
case "L":
repeat = curByte
}
}
for j := byte(0); j < repeat; j++ {
pixels = append(pixels, *pixel)
}
}
return &Image{pixels, header.LineWidth}, nil
}
func main() {
}
Александър Иванов
  • Непроверено
  • 0 успешни тест(а)
  • 6 неуспешни тест(а)
Александър Иванов
package main
import (
"fmt"
"strings"
)
type Header struct {
Format string
LineWidth int
Encoding string
}
func (h *Header) withAlphaChanel() bool {
return strings.Contains(h.Format, "A")
}
type Color struct {
Red, Green, Blue byte
}
func (c *Color) String() string {
return fmt.Sprintf("Red: %s, Green: %s, Blue: %s", c.Red, c.Green, c.Blue)
}
type Pixel struct {
red, green, blue, alpha float64
}
func (p *Pixel) Color() (color Color) {
color.Red = p.buildByteColor(p.red)
color.Green = p.buildByteColor(p.green)
color.Blue = p.buildByteColor(p.blue)
return color
}
func (p *Pixel) buildByteColor(value float64) byte {
return byte(value * p.alpha * 255 + 0.5)
}
func (p *Pixel) set(property string, value float64) {
switch property {
case "R":
p.red = value
case "G":
p.green = value
case "B":
p.blue = value
case "A":
p.alpha = value
}
}
type Image struct {
header Header
pixels []Pixel
}
func (i *Image) InspectPixel(column, row int) (p Pixel, err error) {
position := row*i.lineWidth() + column
if len(i.pixels) <= position || column > i.lineWidth() {
e := new(NoSuchPixel)
e.row = row
e.column = column
err = e
return
}
p = i.pixels[position]
return
}
func (i *Image) initFromBytes(data []byte) {
step := len(i.header.Format)
isRLE := false
if i.header.Encoding == "RLE" {
isRLE = true
step += 1
}
for j := 0; j < len(data); j += step {
if isRLE {
pixel := i.buildPixel(data[j+1:])
for n := 0; n < int(data[j]); n++ {
i.addPixel(pixel)
}
} else {
i.addPixel(i.buildPixel(data[j:]))
}
}
}
func (i *Image) lineWidth() int {
return i.header.LineWidth
}
func (i *Image) withAlphaChanel() bool {
return i.header.withAlphaChanel()
}
func (i *Image) addPixel(pixel Pixel) {
i.pixels = append(i.pixels, pixel)
}
func (i *Image) buildPixel(data []byte) (p Pixel) {
for j, property := range i.header.Format {
p.set(string(property), float64(data[j])/255.0)
}
if !i.withAlphaChanel() {
p.alpha = 1.0
}
return
}
func ParseImage(data []byte, header Header) (image Image, err error) {
image.header = header
defer func() {
if r := recover(); r != nil {
e := new(ParsingError)
e.description = r.(error).Error()
err = e
return
}
}()
image.initFromBytes(data)
return
}
type ParsingError struct {
description string
}
func (e *ParsingError) Error() string {
return "ParsingError: " + e.description
}
type NoSuchPixel struct {
row int
column int
}
func (e *NoSuchPixel) Error() string {
return fmt.Sprintf("There is no pixel on row = %d and column = %d", e.row, e.column)
}
Живко Чобанов
  • Непроверено
  • 0 успешни тест(а)
  • 6 неуспешни тест(а)
Живко Чобанов
package main
//import "fmt"
type Header struct {
Format string
LineWidth int
Encoding string
}
type Pixel struct {
red byte
green byte
blue byte
alphaRatio float32
}
type RGB struct {
Red byte
Green byte
Blue byte
}
type pixelBytes []byte
type Image struct {
pixelsMatrix [][]pixelBytes
format format
}
type format struct {
numberOfBytesPerPixel int
redByteId int
greenByteId int
blueByteId int
alphaByteId int
}
func ParseImage(bytes []byte, header Header) (img Image) {
img.format.set(header)
var bytesInNoneEncoding []byte
switch header.Encoding {
case "RLE":
bytesInNoneEncoding = RLEBytesToNoneEncodingBytes(bytes, img.format.numberOfBytesPerPixel)
case "None":
bytesInNoneEncoding = bytes
default:
panic("Undefined Encoding in Header")
}
ParseImageWithNoneEncoding(bytesInNoneEncoding, header, &img)
return
}
func RLEBytesToNoneEncodingBytes(bytes []byte, bytesPerPixelInNoneEncoding int) (noneEncodingBytes []byte) {
type position struct {
none int
RLE int
}
var firstNotDone, bytesPerPixel, colorByteId position
bytesPerPixel.none = bytesPerPixelInNoneEncoding
bytesPerPixel.RLE = 1 + bytesPerPixel.none
noneEncodingBytes = make([]byte, len(bytes))
for byteWordId := 0; byteWordId < len(bytes)/bytesPerPixel.RLE; byteWordId++ {
firstNotDone.RLE = byteWordId * bytesPerPixel.RLE
numberOfRepetitions := int(bytes[firstNotDone.RLE])
for pixelRepetitionId := 0; pixelRepetitionId < numberOfRepetitions; pixelRepetitionId++ {
for colorByteIdInWord := 0; colorByteIdInWord < bytesPerPixel.none; colorByteIdInWord++ {
colorByteId.none = firstNotDone.none + bytesPerPixel.none*pixelRepetitionId + colorByteIdInWord
colorByteId.RLE = firstNotDone.RLE + 1 + colorByteIdInWord
if colorByteId.none < len(noneEncodingBytes) {
noneEncodingBytes[colorByteId.none] = bytes[colorByteId.RLE]
} else {
noneEncodingBytes = append(noneEncodingBytes, bytes[colorByteId.RLE])
}
}
firstNotDone.none += bytesPerPixelInNoneEncoding
}
}
//fmt.Printf("%v+", bytes)
//fmt.Printf("%v+", noneEncodingBytes)
return
}
func ParseImageWithNoneEncoding(bytes []byte, header Header, img *Image) {
numberOfCols := header.LineWidth
numberOfRows := len(bytes) / (img.format.numberOfBytesPerPixel * numberOfCols)
img.pixelsMatrix = make([][]pixelBytes, numberOfRows)
for rowId := 0; rowId < numberOfRows; rowId++ {
img.pixelsMatrix[rowId] = make([]pixelBytes, numberOfCols)
for colId := 0; colId < numberOfCols; colId++ {
firstByteId := (rowId*numberOfCols + colId) * img.format.numberOfBytesPerPixel
outerLimitByteId := firstByteId + img.format.numberOfBytesPerPixel
img.pixelsMatrix[rowId][colId] = bytes[firstByteId:outerLimitByteId]
}
}
}
func (imageFormat *format) set(header Header) {
imageFormat.numberOfBytesPerPixel = len(header.Format)
for byteId, letter := range header.Format {
switch {
case letter == 'R':
imageFormat.redByteId = byteId
case letter == 'G':
imageFormat.greenByteId = byteId
case letter == 'B':
imageFormat.blueByteId = byteId
case letter == 'A':
imageFormat.alphaByteId = byteId
default:
panic("Undefined Header.Format: " + string(letter))
}
}
//fmt.Printf("%+v", imageFormat)
}
func (img Image) InspectPixel(y int, x int) *Pixel {
pixelBytes := img.pixelsMatrix[x][y]
var alphaRatio float32
if img.hasAlpha() {
alphaRatio = float32(pixelBytes[img.format.alphaByteId]) / 255
} else {
alphaRatio = 1
}
//fmt.Printf("%+v", pixelBytes[img.format.greenByteId])
return &Pixel{
pixelBytes[img.format.redByteId],
pixelBytes[img.format.greenByteId],
pixelBytes[img.format.blueByteId],
alphaRatio,
}
}
func (img Image) hasAlpha() bool {
if img.format.numberOfBytesPerPixel == 4 {
return true
}
return false
}
func (px *Pixel) Color() (rgb RGB) {
rgb.Red = px.red
rgb.Green = px.green
rgb.Blue = px.blue
ApplyAlpha := func(the_byte *byte) { *the_byte = byte(float32(*the_byte) * px.alphaRatio) }
ApplyAlpha(&rgb.Red)
ApplyAlpha(&rgb.Green)
ApplyAlpha(&rgb.Blue)
return
}
Димитър Дишев
  • Непроверено
  • 6 успешни тест(а)
  • 0 неуспешни тест(а)
Димитър Дишев
package main
import (
"math"
"strings"
)
type Header struct {
format string
lineWidth int
encoding string
}
type Colors struct {
Red byte
Green byte
Blue byte
}
type Pixel struct {
numOfIndenticalPxl int
Red float64
Green float64
Blue float64
Alpha float64
byteColorData []byte
}
func (p *Pixel) Color() Colors {
clrs := new(Colors)
clrs.Red = p.byteColorData[0]
clrs.Green = p.byteColorData[1]
clrs.Blue = p.byteColorData[2]
return *clrs
}
func RoundViaFloat(x float64, prec int) float64 {
var rounder float64
pow := math.Pow(10, float64(prec))
intermed := x * pow
_, frac := math.Modf(intermed)
if frac >= 0.5 {
rounder = math.Ceil(intermed)
} else {
rounder = math.Floor(intermed)
}
return rounder / pow
}
func (p *Pixel) precomputing() {
p.Red = p.Red * p.Alpha
p.Green = p.Green * p.Alpha
p.Blue = p.Blue * p.Alpha
red := byte(int32(RoundViaFloat(p.Red, 0)))
green := byte(int32(RoundViaFloat(p.Green, 0)))
blue := byte(int32(RoundViaFloat(p.Blue, 0)))
p.byteColorData = []byte{red, green, blue}
}
type Image struct {
data []Pixel
header Header
}
type myError struct {
errorMessage string
}
func (m myError) Error() string {
return m.errorMessage
}
func (i Image) InspectPixel(x int, y int) (*Pixel, *myError) {
errHandle := new(myError)
catchPxlIndx := 0
if x > i.header.lineWidth {
errHandle.errorMessage = "You are looking for pixel out of the grid"
return nil, errHandle
} else {
searchedIndex := y*i.header.lineWidth + x
for _, value := range i.data {
catchPxlIndx += value.numOfIndenticalPxl
if catchPxlIndx > searchedIndex {
return &value, nil
}
continue
}
errHandle.errorMessage = "You are looking for pixel out of the grid"
return nil, errHandle
}
}
func calculateAlpha(a float64) float64 {
var temp float64 = a / 255
return temp
}
func checkInput(data []byte, header Header) bool {
if header.encoding != "RLE" && header.encoding != "None" {
return false
}
for i := 0; i < len(header.format); i++ {
temp := header.format[i]
if temp == 'R' || temp == 'G' || temp == 'B' || temp == 'A' {
continue
}
return false
}
if header.encoding == "RLE" {
if len(data)%(len(header.format)+1) != 0 {
return false
}
} else if header.encoding == "None" {
if len(data)%len(header.format) != 0 {
return false
}
}
return true
}
func ParseImage(data []byte, header Header) (*Image, *myError) {
if !checkInput(data, header) {
err := new(myError)
err.errorMessage = "Something went wrong with your input, check your data array, encoding and format strings"
return nil, err
}
pixelSize := 0
encdng := false
if header.encoding == "RLE" {
encdng = true
if len(header.format) == 3 {
pixelSize = 4
} else if len(header.format) == 4 {
pixelSize = 5
}
} else {
pixelSize = len(header.format)
}
pixelsCount := len(data) / pixelSize
pImage := new(Image)
pImage.header.lineWidth = header.lineWidth
for i := 0; i < pixelsCount; i++ {
test := data[:pixelSize]
data = data[pixelSize:]
pixel := new(Pixel)
if !encdng {
pixel.numOfIndenticalPxl = 1
}
indexCorrector := 0
for j := 0; j < pixelSize; j++ {
if encdng && j == 0 {
pixel.numOfIndenticalPxl = int(test[0])
indexCorrector = 1
continue
}
switch header.format[j-indexCorrector] {
case 'R':
pixel.Red = float64(test[j])
case 'G':
pixel.Green = float64(test[j])
case 'B':
pixel.Blue = float64(test[j])
case 'A':
pixel.Alpha = float64(test[j])
}
}
if strings.Index(header.format, "A") == -1 {
pixel.Alpha = 1.0
} else {
pixel.Alpha = calculateAlpha(pixel.Alpha)
}
pixel.precomputing()
pImage.data = append(pImage.data, *pixel)
}
return pImage, nil
}
Дойчин Атанасов
  • Непроверено
  • 6 успешни тест(а)
  • 0 неуспешни тест(а)
Дойчин Атанасов
package main
import (
"fmt"
)
type Header struct {
Format string
LineWidth uint
Encoding string
}
type Pixel struct {
Red byte
Green byte
Blue byte
Alpha byte
needsPremultiply bool
}
// In case *someone* calls us with a wrong method name
// I will penalise him/her with one extra copy
func (pixel *Pixel) Color() Pixel {
return pixel.Colour()
}
// quack quack!
func (pixel *Pixel) Colour() Pixel {
return *pixel
}
func (pixel *Pixel) premultiply() {
if !pixel.needsPremultiply {
return
}
pixel.needsPremultiply = false
pixel.Red = alphaBlend(pixel.Red, pixel.Alpha)
pixel.Green = alphaBlend(pixel.Green, pixel.Alpha)
pixel.Blue = alphaBlend(pixel.Blue, pixel.Alpha)
}
func (pixel Pixel) String() string {
return fmt.Sprintf("Red: %d, Green: %d, Blue: %d", pixel.Red, pixel.Green,
pixel.Blue)
}
type Image struct {
header Header
data []Pixel
}
func (img *Image) InspectPixel(x uint, y uint) (*Pixel, *ImageError) {
index := y*img.header.LineWidth + x
if index >= (uint)(len(img.data)) {
return nil, newImageError("Index out of range")
}
return &img.data[index], nil
}
type ImageError string
func (e ImageError) Error() string {
return string(e)
}
func newImageError(message string) *ImageError {
return (*ImageError)(&message)
}
func isHeaderValid(header Header) (err *ImageError) {
formatLen := len(header.Format)
if formatLen < 3 || formatLen > 4 {
err = newImageError("Header is too long or too short")
return
}
formatInt := 0
for _, char := range header.Format {
switch char {
case 'R':
formatInt += 10
case 'B':
formatInt += 100
case 'G':
formatInt += 1000
case 'A':
formatInt += 1
default:
err = newImageError("Wrong letter in header format")
return
}
}
if formatInt != 1110 && formatInt != 1111 {
err = newImageError("Header does not have red, green or blue component")
return
}
if header.Encoding != "None" && header.Encoding != "RLE" {
err = newImageError("Wrong header encoding. Should be None or RLE")
return
}
return
}
func ParseImage(data []byte, header Header) (*Image, *ImageError) {
image := new(Image)
if headerError := isHeaderValid(header); headerError != nil {
return nil, headerError
}
image.header = header
var pixel *Pixel
parsers := make(map[string]func () (*ImageError))
parsers["None"] = func() (*ImageError) {
formatLen := len(header.Format)
if len(data) % formatLen != 0 {
return newImageError("Not enough data for whole pixel")
}
for index, colourIntesity := range data {
formatIndex := index % formatLen
if formatIndex == 0 {
pixel = new(Pixel)
}
colour := header.Format[formatIndex]
switch colour {
case 'R':
pixel.Red = colourIntesity
case 'G':
pixel.Green = colourIntesity
case 'B':
pixel.Blue = colourIntesity
case 'A':
pixel.Alpha = colourIntesity
pixel.needsPremultiply = true
}
if formatIndex == formatLen-1 {
pixel.premultiply()
image.data = append(image.data, *pixel)
}
}
return nil
}
parsers["RLE"] = func() (*ImageError) {
var pixelsCount int
var colourIntesity byte
var colourIndex int
for i := 0; i < len(data); i++ {
pixelsCount = (int)(data[i])
pixel = new(Pixel)
for formatIndex := range(header.Format) {
colourIndex = i + 1 + formatIndex
if colourIndex >= len(data) {
return newImageError("Not enough data for pixel")
}
colourIntesity = data[colourIndex]
colour := header.Format[formatIndex]
switch colour {
case 'R':
pixel.Red = colourIntesity
case 'G':
pixel.Green = colourIntesity
case 'B':
pixel.Blue = colourIntesity
case 'A':
pixel.Alpha = colourIntesity
pixel.needsPremultiply = true
}
}
pixel.premultiply()
for i := 0; i < pixelsCount; i++ {
image.data = append(image.data, *pixel)
}
i += len(header.Format)
}
return nil
}
if err := parsers[header.Encoding](); err != nil {
return nil, err
}
pixelsCount := len(image.data)
if pixelsCount > 0 && (uint)(pixelsCount)%header.LineWidth > 0 {
return nil, newImageError("Not enough data for a whole row")
}
return image, nil
}
func alphaBlend(colour byte, alpha byte) byte {
return (byte)(((int)(colour)*(int)(alpha) + 127) / 255)
}