1 //------------------------------------------------------------------------------
2 // emPnmImageFileModel.cpp
3 //
4 // Copyright (C) 2004-2009,2014,2018-2019 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20 
21 #include <emPnm/emPnmImageFileModel.h>
22 
23 
Acquire(emContext & context,const emString & name,bool common)24 emRef<emPnmImageFileModel> emPnmImageFileModel::Acquire(
25 	emContext & context, const emString & name, bool common
26 )
27 {
28 	EM_IMPL_ACQUIRE(emPnmImageFileModel,context,name,common)
29 }
30 
31 
emPnmImageFileModel(emContext & context,const emString & name)32 emPnmImageFileModel::emPnmImageFileModel(
33 	emContext & context, const emString & name
34 )
35 	: emImageFileModel(context,name)
36 {
37 	L=NULL;
38 }
39 
40 
~emPnmImageFileModel()41 emPnmImageFileModel::~emPnmImageFileModel()
42 {
43 	emPnmImageFileModel::QuitLoading();
44 	emPnmImageFileModel::QuitSaving();
45 }
46 
47 
TryStartLoading()48 void emPnmImageFileModel::TryStartLoading()
49 {
50 	errno=0;
51 
52 	L=new LoadingState;
53 	memset(L,0,sizeof(LoadingState));
54 
55 	L->File=fopen(GetFilePath(),"rb");
56 	if (!L->File) goto Err;
57 
58 	if (Read8()!='P') goto Err;
59 	L->Format=ReadDecimal();
60 	if (L->Format<1 || L->Format>6) goto Err;
61 
62 	L->Width=ReadDecimal();
63 	L->Height=ReadDecimal();
64 	if (L->Width<1 || L->Height<1) goto Err;
65 	if (L->Width>0x7fffff || L->Height>0x7fffff) goto Err;
66 
67 	if (L->Format==2 || L->Format==3 || L->Format==5 || L->Format==6) {
68 		L->MaxVal=ReadDecimal();
69 		if (L->MaxVal<1 || L->MaxVal>65535) goto Err;
70 	}
71 
72 	return;
73 
74 Err:
75 	if (errno) throw emException("%s",emGetErrorText(errno).Get());
76 	else throw emException("PNM format error");
77 }
78 
79 
TryContinueLoading()80 bool emPnmImageFileModel::TryContinueLoading()
81 {
82 	unsigned char * map, * mapEnd;
83 	int i,n,v;
84 
85 	errno=0;
86 
87 	if (L->Format==3 || L->Format==6) n=3; else n=1;
88 
89 	if (!L->ImagePrepared) {
90 		Image.Setup(L->Width,L->Height,n);
91 		switch (L->Format) {
92 		case 1: FileFormatInfo="PNM P1 (PBM ASCII)"; break;
93 		case 2: FileFormatInfo="PNM P2 (PGM ASCII)"; break;
94 		case 3: FileFormatInfo="PNM P3 (PPM ASCII)"; break;
95 		case 4: FileFormatInfo="PNM P4 (PBM RAW)"; break;
96 		case 5: FileFormatInfo="PNM P5 (PGM RAW)"; break;
97 		case 6: FileFormatInfo="PNM P6 (PPM RAW)"; break;
98 		}
99 		Signal(ChangeSignal);
100 		L->ImagePrepared=true;
101 		return false;
102 	}
103 
104 	if (L->NextY>=L->Height) {
105 		return true;
106 	}
107 
108 	map=Image.GetWritableMap()+L->NextY*(size_t)L->Width*n;
109 	mapEnd=map+n*L->Width;
110 
111 	if (L->Format==1) {
112 		for (; map<mapEnd; map++) {
113 			v=ReadDigit(true);
114 			if (v<0 || v>1) goto Err;
115 			map[0]=(unsigned char)(v?0:255);
116 		}
117 	}
118 	else if (L->Format==4) {
119 		for (i=0, v=0; map<mapEnd; map++, i=(i+1)&7) {
120 			if (i==0) {
121 				v=Read8();
122 				if (v<0) goto Err;
123 			}
124 			map[0]=(unsigned char)((v&(128>>i))?0:255);
125 		}
126 	}
127 	else if (L->Format==2 || L->Format==5) {
128 		for (; map<mapEnd; map++) {
129 			v=ReadVal();
130 			if (v<0) goto Err;
131 			map[0]=(unsigned char)v;
132 		}
133 	}
134 	else if (L->Format==3 || L->Format==6) {
135 		for (; map<mapEnd; map+=3) {
136 			v=ReadVal();
137 			if (v<0) goto Err;
138 			map[0]=(unsigned char)v;
139 			v=ReadVal();
140 			if (v<0) goto Err;
141 			map[1]=(unsigned char)v;
142 			v=ReadVal();
143 			if (v<0) goto Err;
144 			map[2]=(unsigned char)v;
145 		}
146 	}
147 
148 	Signal(ChangeSignal);
149 
150 	if (ferror(L->File)) goto Err;
151 
152 	L->NextY++;
153 	if (L->NextY>=L->Height) {
154 		return true;
155 	}
156 
157 	return false;
158 
159 Err:
160 	if (errno) throw emException("%s",emGetErrorText(errno).Get());
161 	else throw emException("PNM format error");
162 }
163 
164 
QuitLoading()165 void emPnmImageFileModel::QuitLoading()
166 {
167 	if (L) {
168 		if (L->File) fclose(L->File);
169 		delete L;
170 		L=NULL;
171 	}
172 }
173 
174 
TryStartSaving()175 void emPnmImageFileModel::TryStartSaving()
176 {
177 	throw emException("emPnmImageFileModel: Saving not implemented.");
178 }
179 
180 
TryContinueSaving()181 bool emPnmImageFileModel::TryContinueSaving()
182 {
183 	return true;
184 }
185 
186 
QuitSaving()187 void emPnmImageFileModel::QuitSaving()
188 {
189 }
190 
191 
CalcMemoryNeed()192 emUInt64 emPnmImageFileModel::CalcMemoryNeed()
193 {
194 	emUInt64 m;
195 
196 	if (L) {
197 		m=((emUInt64)L->Width)*L->Height;
198 		if (L->Format==3 || L->Format==6) m*=3;
199 		return m;
200 	}
201 	else {
202 		return ((emUInt64)Image.GetWidth())*
203 		       Image.GetHeight()*
204 		       Image.GetChannelCount();
205 	}
206 }
207 
208 
CalcFileProgress()209 double emPnmImageFileModel::CalcFileProgress()
210 {
211 	if (L && L->Height>0) {
212 		return 100.0*L->NextY/L->Height;
213 	}
214 	else {
215 		return 0.0;
216 	}
217 }
218 
219 
Read8()220 int emPnmImageFileModel::Read8()
221 {
222 	return (unsigned char)fgetc(L->File);
223 }
224 
225 
Read16()226 int emPnmImageFileModel::Read16()
227 {
228 	int i;
229 
230 	i=Read8()<<8;
231 	i|=Read8();
232 	return i;
233 }
234 
235 
ReadDigit(bool allowSpace)236 int emPnmImageFileModel::ReadDigit(bool allowSpace)
237 {
238 	int c;
239 
240 	for (;;) {
241 		c=fgetc(L->File);
242 		if (c>='0' && c<='9') return c-'0';
243 		if (c=='#') {
244 			do {
245 				c=fgetc(L->File);
246 				if (c<0) return -1;
247 			} while (c!=0x0a);
248 		}
249 		if (!allowSpace || c<0 || c>0x20) return -1;
250 	}
251 }
252 
253 
ReadDecimal()254 int emPnmImageFileModel::ReadDecimal()
255 {
256 	int i,j;
257 
258 	i=ReadDigit(true);
259 	if (i>=0) {
260 		for (;;) {
261 			j=ReadDigit(false);
262 			if (j<0) break;
263 			i=i*10+j;
264 		}
265 	}
266 	return i;
267 }
268 
269 
ReadVal()270 int emPnmImageFileModel::ReadVal()
271 {
272 	int v;
273 
274 	if (L->Format<=3) v=ReadDecimal();
275 	else if (L->MaxVal<=255) v=Read8();
276 	else v=Read16();
277 	if (v<0 || v>L->MaxVal) return -1;
278 	return (v*255+L->MaxVal/2)/L->MaxVal;
279 }
280