1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  */
16 
17 #include "gfx_common.h"
18 #include "tfb_draw.h"
19 #include "drawcmd.h"
20 #include "libs/log.h"
21 #include "libs/memlib.h"
22 
23 
24 static const HOT_SPOT NullHs = {0, 0};
25 
26 void
TFB_DrawScreen_Line(int x1,int y1,int x2,int y2,Color color,DrawMode mode,SCREEN dest)27 TFB_DrawScreen_Line (int x1, int y1, int x2, int y2, Color color,
28 		DrawMode mode, SCREEN dest)
29 {
30 	TFB_DrawCommand DC;
31 
32 	DC.Type = TFB_DRAWCOMMANDTYPE_LINE;
33 	DC.data.line.x1 = x1;
34 	DC.data.line.y1 = y1;
35 	DC.data.line.x2 = x2;
36 	DC.data.line.y2 = y2;
37 	DC.data.line.color = color;
38 	DC.data.line.drawMode = mode;
39 	DC.data.line.destBuffer = dest;
40 
41 	TFB_EnqueueDrawCommand (&DC);
42 }
43 
44 void
TFB_DrawScreen_Rect(RECT * rect,Color color,DrawMode mode,SCREEN dest)45 TFB_DrawScreen_Rect (RECT *rect, Color color, DrawMode mode, SCREEN dest)
46 {
47 	RECT locRect;
48 	TFB_DrawCommand DC;
49 
50 	if (!rect)
51 	{
52 		locRect.corner.x = locRect.corner.y = 0;
53 		locRect.extent.width = ScreenWidth;
54 		locRect.extent.height = ScreenHeight;
55 		rect = &locRect;
56 	}
57 
58 	DC.Type = TFB_DRAWCOMMANDTYPE_RECTANGLE;
59 	DC.data.rect.rect = *rect;
60 	DC.data.rect.color = color;
61 	DC.data.rect.drawMode = mode;
62 	DC.data.rect.destBuffer = dest;
63 
64 	TFB_EnqueueDrawCommand (&DC);
65 }
66 
67 void
TFB_DrawScreen_Image(TFB_Image * img,int x,int y,int scale,int scaleMode,TFB_ColorMap * cmap,DrawMode mode,SCREEN dest)68 TFB_DrawScreen_Image (TFB_Image *img, int x, int y, int scale,
69 		int scaleMode, TFB_ColorMap *cmap, DrawMode mode, SCREEN dest)
70 {
71 	TFB_DrawCommand DC;
72 
73 	DC.Type = TFB_DRAWCOMMANDTYPE_IMAGE;
74 	DC.data.image.image = img;
75 	DC.data.image.colormap = cmap;
76 	DC.data.image.x = x;
77 	DC.data.image.y = y;
78 	DC.data.image.scale = (scale == GSCALE_IDENTITY) ? 0 : scale;
79 	DC.data.image.scaleMode = scaleMode;
80 	DC.data.image.drawMode = mode;
81 	DC.data.image.destBuffer = dest;
82 
83 	TFB_EnqueueDrawCommand (&DC);
84 }
85 
86 void
TFB_DrawScreen_FilledImage(TFB_Image * img,int x,int y,int scale,int scaleMode,Color color,DrawMode mode,SCREEN dest)87 TFB_DrawScreen_FilledImage (TFB_Image *img, int x, int y, int scale,
88 		int scaleMode, Color color, DrawMode mode, SCREEN dest)
89 {
90 	TFB_DrawCommand DC;
91 
92 	DC.Type = TFB_DRAWCOMMANDTYPE_FILLEDIMAGE;
93 	DC.data.filledimage.image = img;
94 	DC.data.filledimage.x = x;
95 	DC.data.filledimage.y = y;
96 	DC.data.filledimage.scale = (scale == GSCALE_IDENTITY) ? 0 : scale;
97 	DC.data.filledimage.scaleMode = scaleMode;
98 	DC.data.filledimage.color = color;
99 	DC.data.filledimage.drawMode = mode;
100 	DC.data.filledimage.destBuffer = dest;
101 
102 	TFB_EnqueueDrawCommand (&DC);
103 }
104 
105 void
TFB_DrawScreen_FontChar(TFB_Char * fontChar,TFB_Image * backing,int x,int y,DrawMode mode,SCREEN dest)106 TFB_DrawScreen_FontChar (TFB_Char *fontChar, TFB_Image *backing,
107 		int x, int y, DrawMode mode, SCREEN dest)
108 {
109 	TFB_DrawCommand DC;
110 
111 	DC.Type = TFB_DRAWCOMMANDTYPE_FONTCHAR;
112 	DC.data.fontchar.fontchar = fontChar;
113 	DC.data.fontchar.backing = backing;
114 	DC.data.fontchar.x = x;
115 	DC.data.fontchar.y = y;
116 	DC.data.fontchar.drawMode = mode;
117 	DC.data.fontchar.destBuffer = dest;
118 
119 	TFB_EnqueueDrawCommand (&DC);
120 }
121 
122 void
TFB_DrawScreen_CopyToImage(TFB_Image * img,const RECT * r,SCREEN src)123 TFB_DrawScreen_CopyToImage (TFB_Image *img, const RECT *r, SCREEN src)
124 {
125 	TFB_DrawCommand DC;
126 
127 	DC.Type = TFB_DRAWCOMMANDTYPE_COPYTOIMAGE;
128 	DC.data.copytoimage.rect = *r;
129 	DC.data.copytoimage.image = img;
130 	DC.data.copytoimage.srcBuffer = src;
131 
132 	TFB_EnqueueDrawCommand (&DC);
133 }
134 
135 void
TFB_DrawScreen_Copy(const RECT * r,SCREEN src,SCREEN dest)136 TFB_DrawScreen_Copy (const RECT *r, SCREEN src, SCREEN dest)
137 {
138 	RECT locRect;
139 	TFB_DrawCommand DC;
140 
141 	if (!r)
142 	{
143 		locRect.corner.x = locRect.corner.y = 0;
144 		locRect.extent.width = ScreenWidth;
145 		locRect.extent.height = ScreenHeight;
146 		r = &locRect;
147 	}
148 
149 	DC.Type = TFB_DRAWCOMMANDTYPE_COPY;
150 	DC.data.copy.rect = *r;
151 	DC.data.copy.srcBuffer = src;
152 	DC.data.copy.destBuffer = dest;
153 
154 	TFB_EnqueueDrawCommand (&DC);
155 }
156 
157 void
TFB_DrawScreen_SetMipmap(TFB_Image * img,TFB_Image * mmimg,int hotx,int hoty)158 TFB_DrawScreen_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, int hoty)
159 {
160 	TFB_DrawCommand DC;
161 
162 	DC.Type = TFB_DRAWCOMMANDTYPE_SETMIPMAP;
163 	DC.data.setmipmap.image = img;
164 	DC.data.setmipmap.mipmap = mmimg;
165 	DC.data.setmipmap.hotx = hotx;
166 	DC.data.setmipmap.hoty = hoty;
167 
168 	TFB_EnqueueDrawCommand (&DC);
169 }
170 
171 void
TFB_DrawScreen_DeleteImage(TFB_Image * img)172 TFB_DrawScreen_DeleteImage (TFB_Image *img)
173 {
174 	if (img)
175 	{
176 		TFB_DrawCommand DC;
177 
178 		DC.Type = TFB_DRAWCOMMANDTYPE_DELETEIMAGE;
179 		DC.data.deleteimage.image = img;
180 
181 		TFB_EnqueueDrawCommand (&DC);
182 	}
183 }
184 
185 void
TFB_DrawScreen_DeleteData(void * data)186 TFB_DrawScreen_DeleteData (void *data)
187 		// data must be a result of HXalloc() call
188 {
189 	if (data)
190 	{
191 		TFB_DrawCommand DC;
192 
193 		DC.Type = TFB_DRAWCOMMANDTYPE_DELETEDATA;
194 		DC.data.deletedata.data = data;
195 
196 		TFB_EnqueueDrawCommand (&DC);
197 	}
198 }
199 
200 void
TFB_DrawScreen_WaitForSignal(void)201 TFB_DrawScreen_WaitForSignal (void)
202 {
203 	TFB_DrawCommand DrawCommand;
204 	Semaphore s;
205 	s = GetMyThreadLocal ()->flushSem;
206 	DrawCommand.Type = TFB_DRAWCOMMANDTYPE_SENDSIGNAL;
207 	DrawCommand.data.sendsignal.sem = s;
208 	Lock_DCQ (1);
209 	TFB_BatchReset ();
210 	TFB_EnqueueDrawCommand (&DrawCommand);
211 	Unlock_DCQ();
212 	SetSemaphore (s);
213 }
214 
215 void
TFB_DrawScreen_ReinitVideo(int driver,int flags,int width,int height)216 TFB_DrawScreen_ReinitVideo (int driver, int flags, int width, int height)
217 {
218 	TFB_DrawCommand DrawCommand;
219 	DrawCommand.Type = TFB_DRAWCOMMANDTYPE_REINITVIDEO;
220 	DrawCommand.data.reinitvideo.driver = driver;
221 	DrawCommand.data.reinitvideo.flags = flags;
222 	DrawCommand.data.reinitvideo.width = width;
223 	DrawCommand.data.reinitvideo.height = height;
224 	TFB_EnqueueDrawCommand (&DrawCommand);
225 }
226 
227 void
TFB_DrawScreen_Callback(void (* callback)(void * arg),void * arg)228 TFB_DrawScreen_Callback (void (*callback) (void *arg), void *arg)
229 {
230 	TFB_DrawCommand DrawCommand;
231 	DrawCommand.Type = TFB_DRAWCOMMANDTYPE_CALLBACK;
232 	DrawCommand.data.callback.callback = callback;
233 	DrawCommand.data.callback.arg = arg;
234 	TFB_EnqueueDrawCommand(&DrawCommand);
235 }
236 
237 void
TFB_DrawImage_Line(int x1,int y1,int x2,int y2,Color color,DrawMode mode,TFB_Image * target)238 TFB_DrawImage_Line (int x1, int y1, int x2, int y2, Color color,
239 		DrawMode mode, TFB_Image *target)
240 {
241 	LockMutex (target->mutex);
242 	TFB_DrawCanvas_Line (x1, y1, x2, y2, color, mode, target->NormalImg);
243 	target->dirty = TRUE;
244 	UnlockMutex (target->mutex);
245 }
246 
247 void
TFB_DrawImage_Rect(RECT * rect,Color color,DrawMode mode,TFB_Image * target)248 TFB_DrawImage_Rect (RECT *rect, Color color, DrawMode mode, TFB_Image *target)
249 {
250 	LockMutex (target->mutex);
251 	TFB_DrawCanvas_Rect (rect, color, mode, target->NormalImg);
252 	target->dirty = TRUE;
253 	UnlockMutex (target->mutex);
254 }
255 
256 void
TFB_DrawImage_Image(TFB_Image * img,int x,int y,int scale,int scaleMode,TFB_ColorMap * cmap,DrawMode mode,TFB_Image * target)257 TFB_DrawImage_Image (TFB_Image *img, int x, int y, int scale,
258 		int scaleMode, TFB_ColorMap *cmap, DrawMode mode, TFB_Image *target)
259 {
260 	LockMutex (target->mutex);
261 	TFB_DrawCanvas_Image (img, x, y, scale, scaleMode, cmap,
262 			mode, target->NormalImg);
263 	target->dirty = TRUE;
264 	UnlockMutex (target->mutex);
265 }
266 
267 void
TFB_DrawImage_FilledImage(TFB_Image * img,int x,int y,int scale,int scaleMode,Color color,DrawMode mode,TFB_Image * target)268 TFB_DrawImage_FilledImage (TFB_Image *img, int x, int y, int scale,
269 		int scaleMode, Color color, DrawMode mode, TFB_Image *target)
270 {
271 	LockMutex (target->mutex);
272 	TFB_DrawCanvas_FilledImage (img, x, y, scale, scaleMode, color,
273 			mode, target->NormalImg);
274 	target->dirty = TRUE;
275 	UnlockMutex (target->mutex);
276 }
277 
278 void
TFB_DrawImage_FontChar(TFB_Char * fontChar,TFB_Image * backing,int x,int y,DrawMode mode,TFB_Image * target)279 TFB_DrawImage_FontChar (TFB_Char *fontChar, TFB_Image *backing,
280 		int x, int y, DrawMode mode, TFB_Image *target)
281 {
282 	LockMutex (target->mutex);
283 	TFB_DrawCanvas_FontChar (fontChar, backing, x, y, mode, target->NormalImg);
284 	target->dirty = TRUE;
285 	UnlockMutex (target->mutex);
286 }
287 
288 
289 TFB_Image *
TFB_DrawImage_New(TFB_Canvas canvas)290 TFB_DrawImage_New (TFB_Canvas canvas)
291 {
292 	TFB_Image *img = HMalloc (sizeof (TFB_Image));
293 	img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO);
294 	img->ScaledImg = NULL;
295 	img->MipmapImg = NULL;
296 	img->FilledImg = NULL;
297 	img->colormap_index = -1;
298 	img->colormap_version = 0;
299 	img->NormalHs = NullHs;
300 	img->MipmapHs = NullHs;
301 	img->last_scale_hs = NullHs;
302 	img->last_scale_type = -1;
303 	img->last_scale = 0;
304 	img->dirty = FALSE;
305 	TFB_DrawCanvas_GetExtent (canvas, &img->extent);
306 
307 	if (TFB_DrawCanvas_IsPaletted (canvas))
308 	{
309 		img->NormalImg = canvas;
310 	}
311 	else
312 	{
313 		img->NormalImg = TFB_DrawCanvas_ToScreenFormat (canvas);
314 	}
315 
316 	return img;
317 }
318 
319 TFB_Image*
TFB_DrawImage_CreateForScreen(int w,int h,BOOLEAN withalpha)320 TFB_DrawImage_CreateForScreen (int w, int h, BOOLEAN withalpha)
321 {
322 	TFB_Image* img = HMalloc (sizeof (TFB_Image));
323 	img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO);
324 	img->ScaledImg = NULL;
325 	img->MipmapImg = NULL;
326 	img->FilledImg = NULL;
327 	img->colormap_index = -1;
328 	img->colormap_version = 0;
329 	img->NormalHs = NullHs;
330 	img->MipmapHs = NullHs;
331 	img->last_scale_hs = NullHs;
332 	img->last_scale_type = -1;
333 	img->last_scale = 0;
334 	img->extent.width = w;
335 	img->extent.height = h;
336 
337 	img->NormalImg = TFB_DrawCanvas_New_ForScreen (w, h, withalpha);
338 
339 	return img;
340 }
341 
342 TFB_Image *
TFB_DrawImage_New_Rotated(TFB_Image * img,int angle)343 TFB_DrawImage_New_Rotated (TFB_Image *img, int angle)
344 {
345 	TFB_Canvas dst;
346 	EXTENT size;
347 	TFB_Image* newimg;
348 
349 	/* sanity check */
350 	if (!img->NormalImg)
351 	{
352 		log_add (log_Warning, "TFB_DrawImage_New_Rotated: "
353 				"source canvas is NULL! Failing.");
354 		return NULL;
355 	}
356 
357 	TFB_DrawCanvas_GetRotatedExtent (img->NormalImg, angle, &size);
358 	dst = TFB_DrawCanvas_New_RotationTarget (img->NormalImg, angle);
359 	if (!dst)
360 	{
361 		log_add (log_Warning, "TFB_DrawImage_New_Rotated: "
362 				"rotation target canvas not created! Failing.");
363 		return NULL;
364 	}
365 	TFB_DrawCanvas_Rotate (img->NormalImg, dst, angle, size);
366 
367 	newimg = TFB_DrawImage_New (dst);
368 	return newimg;
369 }
370 
371 void
TFB_DrawImage_SetMipmap(TFB_Image * img,TFB_Image * mmimg,int hotx,int hoty)372 TFB_DrawImage_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, int hoty)
373 {
374 	bool imgpal;
375 	bool mmpal;
376 
377 	if (!img || !mmimg)
378 		return;
379 
380 	LockMutex (img->mutex);
381 	LockMutex (mmimg->mutex);
382 
383 	// Either both images must be using the same colormap, or mipmap image
384 	// must not be paletted. This restriction is due to the current
385 	// implementation of fill-stamp, which replaces the palette with
386 	// fill color.
387 	imgpal = TFB_DrawCanvas_IsPaletted (img->NormalImg);
388 	mmpal = TFB_DrawCanvas_IsPaletted (mmimg->NormalImg);
389 	if (!mmpal || (mmpal && imgpal &&
390 			img->colormap_index == mmimg->colormap_index))
391 	{
392 		img->MipmapImg = mmimg->NormalImg;
393 		img->MipmapHs.x = hotx;
394 		img->MipmapHs.y = hoty;
395 	}
396 	else
397 	{
398 		img->MipmapImg = NULL;
399 	}
400 
401 	UnlockMutex (mmimg->mutex);
402 	UnlockMutex (img->mutex);
403 }
404 
405 void
TFB_DrawImage_Delete(TFB_Image * image)406 TFB_DrawImage_Delete (TFB_Image *image)
407 {
408 	if (image == 0)
409 	{
410 		log_add (log_Warning, "INTERNAL ERROR: Tried to delete a null image!");
411 		/* Should we die here? */
412 		return;
413 	}
414 	LockMutex (image->mutex);
415 
416 	TFB_DrawCanvas_Delete (image->NormalImg);
417 
418 	if (image->ScaledImg)
419 	{
420 		TFB_DrawCanvas_Delete (image->ScaledImg);
421 		image->ScaledImg = 0;
422 	}
423 
424 	if (image->FilledImg)
425 	{
426 		TFB_DrawCanvas_Delete (image->FilledImg);
427 		image->FilledImg = 0;
428 	}
429 
430 	UnlockMutex (image->mutex);
431 	DestroyMutex (image->mutex);
432 
433 	HFree (image);
434 }
435 
436 void
TFB_DrawImage_FixScaling(TFB_Image * image,int target,int type)437 TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type)
438 {
439 	if (image->dirty || !image->ScaledImg ||
440 			target != image->last_scale ||
441 			type != image->last_scale_type)
442 	{
443 		image->dirty = FALSE;
444 		image->ScaledImg = TFB_DrawCanvas_New_ScaleTarget (image->NormalImg,
445 			image->ScaledImg, type, image->last_scale_type);
446 
447 		if (type == TFB_SCALE_NEAREST)
448 			TFB_DrawCanvas_Rescale_Nearest (image->NormalImg,
449 					image->ScaledImg, target, &image->NormalHs,
450 					&image->extent, &image->last_scale_hs);
451 		else if (type == TFB_SCALE_BILINEAR)
452 			TFB_DrawCanvas_Rescale_Bilinear (image->NormalImg,
453 					image->ScaledImg, target, &image->NormalHs,
454 					&image->extent, &image->last_scale_hs);
455 		else
456 			TFB_DrawCanvas_Rescale_Trilinear (image->NormalImg,
457 					image->MipmapImg, image->ScaledImg, target,
458 					&image->NormalHs, &image->MipmapHs,
459 					&image->extent, &image->last_scale_hs);
460 
461 		image->last_scale_type = type;
462 		image->last_scale = target;
463 	}
464 }
465 
466 BOOLEAN
TFB_DrawImage_Intersect(TFB_Image * img1,POINT img1org,TFB_Image * img2,POINT img2org,const RECT * interRect)467 TFB_DrawImage_Intersect (TFB_Image *img1, POINT img1org,
468 		TFB_Image *img2, POINT img2org, const RECT *interRect)
469 {
470 	BOOLEAN ret;
471 
472 	LockMutex (img1->mutex);
473 	LockMutex (img2->mutex);
474 	ret = TFB_DrawCanvas_Intersect (img1->NormalImg, img1org,
475 			img2->NormalImg, img2org, interRect);
476 	UnlockMutex (img2->mutex);
477 	UnlockMutex (img1->mutex);
478 
479 	return ret;
480 }
481 
482 void
TFB_DrawImage_CopyRect(TFB_Image * source,const RECT * srcRect,TFB_Image * target,POINT dstPt)483 TFB_DrawImage_CopyRect (TFB_Image *source, const RECT *srcRect,
484 		TFB_Image *target, POINT dstPt)
485 {
486 	LockMutex (source->mutex);
487 	LockMutex (target->mutex);
488 	TFB_DrawCanvas_CopyRect (source->NormalImg, srcRect,
489 			target->NormalImg, dstPt);
490 	target->dirty = TRUE;
491 	UnlockMutex (target->mutex);
492 	UnlockMutex (source->mutex);
493 }
494