1/*
2 * GO interface to libheif
3 * Copyright (c) 2018 struktur AG, Joachim Bauch <bauch@struktur.de>
4 *
5 * This file is part of heif, an example application using libheif.
6 *
7 * heif is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * heif is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with heif.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21package heif
22
23import (
24	"fmt"
25	"image"
26	"io/ioutil"
27	"os"
28	"path"
29	"testing"
30)
31
32func TestGetVersion(t *testing.T) {
33	version := GetVersion()
34	if version == "" {
35		t.Fatal("Version is missing")
36	}
37}
38
39type decodeTest struct {
40	colorspace Colorspace
41	chroma     Chroma
42}
43
44func CheckHeifImage(t *testing.T, handle *ImageHandle, thumbnail bool) {
45	handle.GetWidth()
46	handle.GetHeight()
47	handle.HasAlphaChannel()
48	handle.HasDepthImage()
49	count := handle.GetNumberOfDepthImages()
50	if ids := handle.GetListOfDepthImageIDs(); len(ids) != count {
51		t.Errorf("Expected %d depth image ids, got %d", count, len(ids))
52	}
53	if !thumbnail {
54		count = handle.GetNumberOfThumbnails()
55		ids := handle.GetListOfThumbnailIDs()
56		if len(ids) != count {
57			t.Errorf("Expected %d thumbnail image ids, got %d", count, len(ids))
58		}
59		for _, id := range ids {
60			if thumb, err := handle.GetThumbnail(id); err != nil {
61				t.Errorf("Could not get thumbnail %d: %s", id, err)
62			} else {
63				CheckHeifImage(t, thumb, true)
64			}
65		}
66	}
67
68	if img, err := handle.DecodeImage(ColorspaceUndefined, ChromaUndefined, nil); err != nil {
69		t.Errorf("Could not decode image: %s", err)
70	} else {
71		img.GetColorspace()
72		img.GetChromaFormat()
73	}
74
75	decodeTests := []decodeTest{
76		decodeTest{ColorspaceYCbCr, Chroma420},
77		decodeTest{ColorspaceYCbCr, Chroma422},
78		decodeTest{ColorspaceYCbCr, Chroma444},
79		decodeTest{ColorspaceRGB, Chroma444},
80		decodeTest{ColorspaceRGB, ChromaInterleavedRGB},
81		decodeTest{ColorspaceRGB, ChromaInterleavedRGBA},
82		decodeTest{ColorspaceRGB, ChromaInterleavedRRGGBB_BE},
83		decodeTest{ColorspaceRGB, ChromaInterleavedRRGGBBAA_BE},
84	}
85	for _, test := range decodeTests {
86		if img, err := handle.DecodeImage(test.colorspace, test.chroma, nil); err != nil {
87			t.Errorf("Could not decode image with %v / %v: %s", test.colorspace, test.chroma, err)
88		} else {
89			img.GetColorspace()
90			img.GetChromaFormat()
91
92			if _, err := img.GetImage(); err != nil {
93				t.Errorf("Could not get image with %v /%v: %s", test.colorspace, test.chroma, err)
94				continue
95			}
96		}
97	}
98}
99
100func CheckHeifFile(t *testing.T, ctx *Context) {
101	if count := ctx.GetNumberOfTopLevelImages(); count != 2 {
102		t.Errorf("Expected %d top level images, got %d", 2, count)
103	}
104	if ids := ctx.GetListOfTopLevelImageIDs(); len(ids) != 2 {
105		t.Errorf("Expected %d top level image ids, got %+v", 2, ids)
106	}
107	if _, err := ctx.GetPrimaryImageID(); err != nil {
108		t.Errorf("Expected a primary image, got %s", err)
109	}
110	if handle, err := ctx.GetPrimaryImageHandle(); err != nil {
111		t.Errorf("Could not get primary image handle: %s", err)
112	} else {
113		if !handle.IsPrimaryImage() {
114			t.Error("Expected primary image")
115		}
116		CheckHeifImage(t, handle, false)
117	}
118}
119
120func TestReadFromFile(t *testing.T) {
121	ctx, err := NewContext()
122	if err != nil {
123		t.Fatalf("Can't create context: %s", err)
124	}
125
126	filename := path.Join("..", "..", "examples", "example.heic")
127	if err := ctx.ReadFromFile(filename); err != nil {
128		t.Fatalf("Can't read from %s: %s", filename, err)
129	}
130
131	CheckHeifFile(t, ctx)
132}
133
134func TestReadFromMemory(t *testing.T) {
135	ctx, err := NewContext()
136	if err != nil {
137		t.Fatalf("Can't create context: %s", err)
138	}
139
140	filename := path.Join("..", "..", "examples", "example.heic")
141	data, err := ioutil.ReadFile(filename)
142	if err != nil {
143		t.Fatalf("Can't read file %s: %s", filename, err)
144	}
145	if err := ctx.ReadFromMemory(data); err != nil {
146		t.Fatalf("Can't read from memory: %s", err)
147	}
148	data = nil // Make sure future processing works if "data" is GC'd
149
150	CheckHeifFile(t, ctx)
151}
152
153func TestReadImage(t *testing.T) {
154	filename := path.Join("..", "..", "examples", "example.heic")
155	fp, err := os.Open(filename)
156	if err != nil {
157		t.Fatalf("Could not open %s: %s", filename, err)
158	}
159	defer fp.Close()
160
161	config, format1, err := image.DecodeConfig(fp)
162	if err != nil {
163		t.Fatalf("Could not load image config from %s: %s", filename, err)
164	}
165	if format1 != "heif" {
166		t.Errorf("Expected format heif, got %s", format1)
167	}
168	if _, err := fp.Seek(0, 0); err != nil {
169		t.Fatalf("Could not seek to start of %s: %s", filename, err)
170	}
171
172	img, format2, err := image.Decode(fp)
173	if err != nil {
174		t.Fatalf("Could not load image from %s: %s", filename, err)
175	}
176	if format2 != "heif" {
177		t.Errorf("Expected format heif, got %s", format2)
178	}
179
180	r := img.Bounds()
181	if config.Width != (r.Max.X-r.Min.X) || config.Height != (r.Max.Y-r.Min.Y) {
182		fmt.Printf("Image size %+v does not match config %+v\n", r, config)
183	}
184}
185