1 //------------------------------------------------------------------------------
2 // emImage.h
3 //
4 // Copyright (C) 2001,2003-2010,2014,2018,2020 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 #ifndef emImage_h
22 #define emImage_h
23 
24 #ifndef emColor_h
25 #include <emCore/emColor.h>
26 #endif
27 
28 #ifndef emATMatrix_h
29 #include <emCore/emATMatrix.h>
30 #endif
31 
32 class emRootContext;
33 class emPainter;
34 
35 
36 //==============================================================================
37 //================================== emImage ===================================
38 //==============================================================================
39 
40 class emImage {
41 
42 public:
43 
44 	// Class for an image with copy-on-write behavior. Such an image
45 	// consists of a three-dimensional array of bytes. The dimensions are X,
46 	// Y and color channel.
47 	//
48 	// An image can have one, two, three or four channels. Each channel has
49 	// one byte per pixel. The meaning of the channels depends on the number
50 	// of channels:
51 	//
52 	//   ChannelCount = 1: Grey image without alpha
53 	//     channel 0: grey components.
54 	//   ChannelCount = 2: Grey image with alpha
55 	//     channel 0: grey components.
56 	//     channel 1: alpha components.
57 	//   ChannelCount = 3: Color image without alpha
58 	//     channel 0: red components.
59 	//     channel 1: green components.
60 	//     channel 2: blue components.
61 	//   ChannelCount = 4: Color image with alpha
62 	//     channel 0: red components.
63 	//     channel 1: green components.
64 	//     channel 2: blue components.
65 	//     channel 3: alpha components.
66 	//
67 	// Like in most computer graphics, the origin of the image is in the
68 	// upper-left corner, the X-axis points to the right, and the Y-axis
69 	// points down. Pixels are areas and not points.
70 
71 	emImage();
72 		// Construct an empty image. It has Width==0, Height==0 and
73 		// ChannelCount==1.
74 
75 	emImage(const emImage & img);
76 		// Construct a copied image.
77 
78 	emImage(int width, int height, int channelCount);
79 		// Construct with the given dimensions. The pixel map is not
80 		// initialized.
81 		// Arguments:
82 		//   width        - Horizontal extent in pixels.
83 		//   height       - Vertical extent in pixels.
84 		//   channelCount - Number of channels (bytes per pixel, 1-4).
85 
86 	~emImage();
87 		// Destructor.
88 
89 	emImage & operator = (const emImage & img);
90 		// Copy an image.
91 
92 	bool operator == (const emImage & image) const;
93 	bool operator != (const emImage & image) const;
94 		// Compare images.
95 
96 	void Setup(int width, int height, int channelCount);
97 		// Set the dimensions of this image. This method does not care
98 		// about preserving the contents.
99 		// Arguments:
100 		//   width        - Horizontal extent in pixels.
101 		//   height       - Vertical extent in pixels.
102 		//   channelCount - Number of channels (bytes per pixel, 1-4).
103 
104 	void SetUserMap(int width, int height, int channelCount, emByte * map);
105 		// Prepare this image for being an interface to a user allocated
106 		// pixel map. For example, this feature could be used for
107 		// exchanging image data between system processes through shared
108 		// memory. You should know about the following semantics: The
109 		// copy-on-write behavior is disabled for such a "user map
110 		// image", and the user map feature is never copied. That means,
111 		// when copying a user map image to another image, a deep copy
112 		// is performed immediately and the target image will be a
113 		// normal image. Each operation which actually changes the
114 		// dimensions of the image, will give up the user map feature
115 		// and revert to the normal behavior. This means for example,
116 		// calling Setup preserves the feature if the dimensions are not
117 		// really changed. Call Clear or '=' if you want to revert to
118 		// the normal behavior in any case.
119 		// Arguments:
120 		//   width        - Horizontal extent of the user map in pixels.
121 		//   height       - Vertical extent of the user map in pixels.
122 		//   channelCount - Number of channels in the user map (bytes
123 		//                  per pixel, 1-4).
124 		//   map          - The user allocated pixel map. It's what you
125 		//                  will get through GetMap(). If channelCount
126 		//                  is 2 or 4, the map address must be aligned
127 		//                  accordingly.
128 
129 	bool HasUserMap() const;
130 		// Ask whether this image interfaces a user allocated pixel map.
131 
132 	void TryParseXpm(const char * const * xpm, int channelCount=-1);
133 		// Set this image by parsing an X Pixmap (XPM). The idea is to
134 		// include an XPM file in the C++ source, and to convert it to
135 		// an emImage at run-time using this method.
136 		// Arguments:
137 		//   xpm          - The X Pixmap data array.
138 		//   channelCount - Channel count of the returned image (1-4),
139 		//                  or -1 to select the best channel count from
140 		//                  the X Pixmap.
141 		// Throws: An error message on failure.
142 
143 	void TryParseTga(const unsigned char * tgaData, size_t tgaSize,
144 	                 int channelCount=-1);
145 		// -------------------------------------------------------------
146 		// This method is deprecated and should not be used any longer.
147 		// -------------------------------------------------------------
148 		// Set this image by parsing a Targa image (TGA). The idea is
149 		// to convert a run-length encoded TGA file to a C source file,
150 		// and to include that source file in the C++ source, and to
151 		// convert it to an emImage at run-time using this function.
152 		// Arguments:
153 		//   tgaData      - Byte array with the contents of a TGA file.
154 		//   tgaSize      - Number of bytes in the array.
155 		//   channelCount - Channel count of the returned image (1-4),
156 		//                  or -1 to select the best channel count from
157 		//                  the TGA image.
158 		// Throws: An error message on failure.
159 
160 	void Clear();
161 		// Empty this image. This is like Setup(0,0,1).
162 
163 	bool IsEmpty() const;
164 		// Ask whether this image is empty. It is empty if at least one
165 		// of width and height is zero.
166 
167 	int GetWidth() const;
168 	int GetHeight() const;
169 	int GetChannelCount() const;
170 		// Get the dimensions of this image.
171 
172 	const emByte * GetMap() const;
173 		// Get a pointer to the pixel map of this image. If ChannelCount
174 		// is 2 or 4, the map address is aligned accordingly. At least
175 		// because of the copy-on-write feature, the pointer is valid
176 		// only until calling any non-const method or operator on this
177 		// image, or giving this image as a non-const argument to any
178 		// call in the world. Hint: Even methods like GetConverted,
179 		// GetCropped and so on may make shallow copies, like the copy
180 		// operator and copy constructor do.
181 		// Index to the map is:
182 		//   (y*GetWidth()+x)*GetChannelCount()+c
183 		// With:
184 		//   x: 0 to GetWidth()-1
185 		//   y: 0 to GetHeight()-1
186 		//   c: 0 to GetChannelCount()-1
187 
188 	emByte * GetWritableMap();
189 		// Like GetMap(), but for modifying the image. The rules for
190 		// validity of the pointer are the same as with GetMap(), but:
191 		// The pointer must not be used for modifying after doing
192 		// something which could have made a shallow copy of this image.
193 
194 	emColor GetPixel(int x, int y) const;
195 	void SetPixel(int x, int y, emColor color);
196 		// Get or set a pixel.
197 
198 	emByte GetPixelChannel(int x, int y, int channel) const;
199 	void SetPixelChannel(int x, int y, int channel, emByte value);
200 		// Get or set one channel of a pixel.
201 
202 	emColor GetPixelInterpolated(double x, double y, double w,
203 	                             double h, emColor bgColor) const;
204 		// Get an average color from a rectangular area. Performs
205 		// bi-linear interpolation or area-sampling.
206 		// Arguments:
207 		//   x,y,w,h - Upper-left corner and size of the rectangle.
208 		//   bgColor - A background color. It is used where the
209 		//             rectangle lies outside the image.
210 		// Returns: The interpolated color.
211 
212 	void Fill(emColor color);
213 	void Fill(int x, int y, int w, int h, emColor color);
214 		// Fill the whole image or a rectangular area with the given
215 		// color.
216 
217 	void FillChannel(int channel, emByte value=0);
218 	void FillChannel(int x, int y, int w, int h, int channel,
219 	                 emByte value);
220 		// Like Fill, but for modifying just a single channel.
221 
222 	void Copy(int x, int y, const emImage & img);
223 	void Copy(int x, int y, const emImage & img,
224 	          int srcX, int srcY, int w, int h);
225 		// Copy a rectangular area of pixels from a source image to this
226 		// image. Source and target may overlap.
227 		// Arguments:
228 		//   x,y           - Upper-left corner of target rectangle on
229 		//                   this image.
230 		//   img           - The source image.
231 		//   srcX,srcY,w,h - Upper-left corner and size of the rectangle
232 		//                   on the source image, which is to be copied.
233 		//                   Without these arguments, the whole source
234 		//                   image is taken.
235 
236 	void CopyChannel(int x, int y, int channel, const emImage & img,
237 	                 int srcChannel);
238 	void CopyChannel(int x, int y, int channel, const emImage & img,
239 	                 int srcX, int srcY, int w, int h, int srcChannel);
240 		// Copy one channel of a rectangular area of pixels from a
241 		// source image to this image. Source and target may overlap.
242 		// Arguments:
243 		//   x,y           - Upper-left corner of target rectangle on
244 		//                   this image.
245 		//   channel       - Target channel on this image.
246 		//   img           - The source image.
247 		//   srcX,srcY,w,h - Upper-left corner and size of the rectangle
248 		//                   on the source image, which is to be copied.
249 		//                   Without these arguments, the whole source
250 		//                   image is taken.
251 		//   srcChannel    - Source channel on the given image.
252 
253 	void CopyTransformed(int x, int y, int w, int h,
254 	                     const emATMatrix & atm, const emImage & img,
255 	                     bool interpolate=false, emColor bgColor=0);
256 		// Copy an image to this image while performing an affine
257 		// transformation. Source and target must not overlap.
258 		// Arguments:
259 		//   x,y,w,h     - A clipping rectangle for the operation on
260 		//                 this image. Exactly this area of pixels is
261 		//                 modified.
262 		//   atm         - A transformation matrix for transforming
263 		//                 source image coordinates to coordinates of
264 		//                 this image.
265 		//   img         - The source image.
266 		//   interpolate - Whether to perform bi-linear interpolation.
267 		//   bgColor     - A color to be used for areas outside the
268 		//                 source image.
269 
270 	emImage GetTransformed(const emATMatrix & atm, bool interpolate=false,
271 	                       emColor bgColor=0, int channelCount=-1) const;
272 		// Get an affine transformed version of this image.
273 		// Arguments:
274 		//   atm          - A transformation matrix for transforming
275 		//                  coordinates of this image to coordinates of
276 		//                  the returned image. Here, any translation
277 		//                  will be ignored.
278 		//   interpolate  - Whether to perform bi-linear interpolation.
279 		//   bgColor      - A color to be used for areas outside the
280 		//                  source image.
281 		//   channelCount - Number of channels in the returned image
282 		//                  (1-4), or -1 for taking the channel count of
283 		//                  this image.
284 		// Returns: The resulting image.
285 
286 	void CalcMinMaxRect(int * pX, int * pY, int * pW, int * pH,
287 	                    emColor bgColor) const;
288 		// Calculate the smallest rectangle which contains all pixels
289 		// which are not equal to the given background color.
290 		// Arguments:
291 		//   pX,pY,pW,pH - Pointers for returning the rectangle.
292 		//   bgColor     - The background color.
293 
294 	void CalcChannelMinMaxRect(int * pX, int * pY, int * pW, int * pH,
295 	                           int channel, emByte bgValue) const;
296 		// Like CalcMinMaxRect, but for a single channel.
297 		// Arguments:
298 		//   pX,pY,pW,pH - Pointers for returning the rectangle.
299 		//   channel     - The channel to be inspected.
300 		//   bgValue     - The channel value of the background color.
301 
302 	void CalcAlphaMinMaxRect(int * pX, int * pY, int * pW,
303 	                         int * pH) const;
304 		// Calculate the smallest rectangle which contains all pixels
305 		// which are not zero in the alpha channel.
306 		// Arguments:
307 		//   pX,pY,pW,pH - Pointers for returning the rectangle.
308 
309 	emImage GetConverted(int channelCount) const;
310 		// Get a copy of this image, converted to the given number of
311 		// channels.
312 
313 	emImage GetCropped(int x, int y, int w, int h,
314 	                   int channelCount=-1) const;
315 		// Get a sub-image of this image.
316 		// Arguments:
317 		//   x,y,w,h      - A rectangle on this image. It's the area to
318 		//                  be copied to the returned image, which will
319 		//                  have the size of that rectangle (after
320 		//                  clipping it by the image boundaries).
321 		//   channelCount - Number of channels of the returned image
322 		//                  (1-4), or -1 for taking the channel count of
323 		//                  this image.
324 		// Returns: The sub-image.
325 
326 	emImage GetCroppedByAlpha(int channelCount=-1) const;
327 		// Like GetCropped with a rectangle returned by
328 		// GetAlphaMinMaxRect.
329 
330 	bool PreparePainter(emPainter * painter, emRootContext & rootContext,
331 	                    double clipX1=0.0, double clipY1=0.0,
332 	                    double clipX2=3E9, double clipY2=3E9,
333 	                    double originX=0.0, double originY=0.0,
334 	                    double scaleX=1.0, double scaleY=1.0);
335 		// Prepare the given painter for painting to this image with
336 		// the given clipping and transformation. IMPORTANT: Currently,
337 		// the image must have 4 channels, otherwise painting would not
338 		// be possible. But the alpha channel cannot be painted and may
339 		// be damaged by the painter. In a near future version of
340 		// emPainter, the image may have to have 3 channels. And in a
341 		// far future version, any channel count may be acceptable. If
342 		// painting is not possible due to the channel count, the
343 		// painter is disabled and false is returned. If you want to
344 		// paint to an image in a portable way, please poll for a
345 		// suitable channel count via IsChannelCountPaintable and
346 		// prepare the image accordingly. After painting, you may
347 		// convert the image to the desired channel count, or you could
348 		// copy channels around. The rules for usability of the painter
349 		// are like with a pointer returned by GetWritableMap().
350 
351 	static bool IsChannelCountPaintable(int channelCount);
352 		// Ask whether PreparePainter would be successful for an image
353 		// of the given channel count.
354 
355 	unsigned int GetDataRefCount() const;
356 		// Get number of references to the data behind this image.
357 
358 	void MakeNonShared();
359 		// This must be called before handing the image to another
360 		// thread.
361 
362 private:
363 
364 	void MakeWritable();
365 	void FreeData();
366 
367 	struct SharedData {
368 		unsigned int RefCount;
369 		int Width;
370 		int Height;
371 		emByte ChannelCount;
372 		emByte IsUsersMap;
373 		emByte * Map;
374 		// From here on comes the non-user map.
375 	};
376 
377 	SharedData * Data;
378 
379 	static SharedData EmptyData;
380 };
381 
382 #ifndef EM_NO_DATA_EXPORT
emImage()383 inline emImage::emImage()
384 {
385 	Data=&EmptyData;
386 }
387 #endif
388 
emImage(const emImage & img)389 inline emImage::emImage(const emImage & img)
390 {
391 	Data=img.Data;
392 	Data->RefCount++;
393 	if (Data->IsUsersMap) MakeWritable();
394 }
395 
~emImage()396 inline emImage::~emImage()
397 {
398 	if (!--Data->RefCount) FreeData();
399 }
400 
401 inline bool emImage::operator != (const emImage & image) const
402 {
403 	return !(*this == image);
404 }
405 
HasUserMap()406 inline bool emImage::HasUserMap() const
407 {
408 	return Data->IsUsersMap!=0;
409 }
410 
411 #ifndef EM_NO_DATA_EXPORT
Clear()412 inline void emImage::Clear()
413 {
414 	if (!--Data->RefCount) FreeData();
415 	Data=&EmptyData;
416 }
417 #endif
418 
IsEmpty()419 inline bool emImage::IsEmpty() const
420 {
421 	return Data->Width==0 || Data->Height==0;
422 }
423 
GetWidth()424 inline int emImage::GetWidth() const
425 {
426 	return Data->Width;
427 }
428 
GetHeight()429 inline int emImage::GetHeight() const
430 {
431 	return Data->Height;
432 }
433 
GetChannelCount()434 inline int emImage::GetChannelCount() const
435 {
436 	return Data->ChannelCount;
437 }
438 
GetMap()439 inline const emByte * emImage::GetMap() const
440 {
441 	return Data->Map;
442 }
443 
GetWritableMap()444 inline emByte * emImage::GetWritableMap()
445 {
446 	if (Data->RefCount>1) MakeWritable();
447 	return Data->Map;
448 }
449 
Fill(emColor color)450 inline void emImage::Fill(emColor color)
451 {
452 	Fill(0,0,Data->Width,Data->Height,color);
453 }
454 
FillChannel(int channel,emByte value)455 inline void emImage::FillChannel(int channel, emByte value)
456 {
457 	FillChannel(0,0,Data->Width,Data->Height,channel,value);
458 }
459 
Copy(int x,int y,const emImage & img)460 inline void emImage::Copy(int x, int y, const emImage & img)
461 {
462 	Copy(x,y,img,0,0,img.Data->Width,img.Data->Height);
463 }
464 
CopyChannel(int x,int y,int channel,const emImage & img,int srcChannel)465 inline void emImage::CopyChannel(
466 	int x, int y, int channel, const emImage & img, int srcChannel
467 )
468 {
469 	CopyChannel(
470 		x,y,channel,img,
471 		0,0,img.Data->Width,img.Data->Height,
472 		srcChannel
473 	);
474 }
475 
GetConverted(int channelCount)476 inline emImage emImage::GetConverted(int channelCount) const
477 {
478 	return GetCropped(0,0,Data->Width,Data->Height,channelCount);
479 }
480 
MakeNonShared()481 inline void emImage::MakeNonShared()
482 {
483 	MakeWritable();
484 }
485 
486 
487 #endif
488