1 #include "img_conv.h"
2 #include "Icon.h"
3 
4 #ifdef __cplusplus
5 extern "C" {
6 #endif
7 
8 
9 typedef void BitBltProc( Byte * src, Byte * dst, int count);
10 typedef BitBltProc *PBitBltProc;
11 
12 static void
bitblt_copy(Byte * src,Byte * dst,int count)13 bitblt_copy( Byte * src, Byte * dst, int count)
14 {
15 	memcpy( dst, src, count);
16 }
17 
18 static void
bitblt_move(Byte * src,Byte * dst,int count)19 bitblt_move( Byte * src, Byte * dst, int count)
20 {
21 	memmove( dst, src, count);
22 }
23 
24 static void
bitblt_or(Byte * src,Byte * dst,int count)25 bitblt_or( Byte * src, Byte * dst, int count)
26 {
27 	while ( count--) *(dst++) |= *(src++);
28 }
29 
30 static void
bitblt_and(Byte * src,Byte * dst,int count)31 bitblt_and( Byte * src, Byte * dst, int count)
32 {
33 	while ( count--) *(dst++) &= *(src++);
34 }
35 
36 static void
bitblt_xor(Byte * src,Byte * dst,int count)37 bitblt_xor( Byte * src, Byte * dst, int count)
38 {
39 	while ( count--) *(dst++) ^= *(src++);
40 }
41 
42 static void
bitblt_not(Byte * src,Byte * dst,int count)43 bitblt_not( Byte * src, Byte * dst, int count)
44 {
45 	while ( count--) *(dst++) = ~(*(src++));
46 }
47 
48 static void
bitblt_notdstand(Byte * src,Byte * dst,int count)49 bitblt_notdstand( Byte * src, Byte * dst, int count)
50 {
51 	while ( count--) {
52 		*dst = ~(*dst) & (*(src++));
53 		dst++;
54 	}
55 }
56 
57 static void
bitblt_notdstor(Byte * src,Byte * dst,int count)58 bitblt_notdstor( Byte * src, Byte * dst, int count)
59 {
60 	while ( count--) {
61 		*dst = ~(*dst) | (*(src++));
62 		dst++;
63 	}
64 }
65 
66 static void
bitblt_notsrcand(Byte * src,Byte * dst,int count)67 bitblt_notsrcand( Byte * src, Byte * dst, int count)
68 {
69 	while ( count--) *(dst++) &= ~(*(src++));
70 }
71 
72 static void
bitblt_notsrcor(Byte * src,Byte * dst,int count)73 bitblt_notsrcor( Byte * src, Byte * dst, int count)
74 {
75 	while ( count--) *(dst++) |= ~(*(src++));
76 }
77 
78 static void
bitblt_notxor(Byte * src,Byte * dst,int count)79 bitblt_notxor( Byte * src, Byte * dst, int count)
80 {
81 	while ( count--) {
82 		*dst = ~( *(src++) ^ (*dst));
83 		dst++;
84 	}
85 }
86 
87 static void
bitblt_notand(Byte * src,Byte * dst,int count)88 bitblt_notand( Byte * src, Byte * dst, int count)
89 {
90 	while ( count--) {
91 		*dst = ~( *(src++) & (*dst));
92 		dst++;
93 	}
94 }
95 
96 static void
bitblt_notor(Byte * src,Byte * dst,int count)97 bitblt_notor( Byte * src, Byte * dst, int count)
98 {
99 	while ( count--) {
100 		*dst = ~( *(src++) | (*dst));
101 		dst++;
102 	}
103 }
104 
105 static void
bitblt_black(Byte * src,Byte * dst,int count)106 bitblt_black( Byte * src, Byte * dst, int count)
107 {
108 	memset( dst, 0, count);
109 }
110 
111 static void
bitblt_white(Byte * src,Byte * dst,int count)112 bitblt_white( Byte * src, Byte * dst, int count)
113 {
114 	memset( dst, 0xff, count);
115 }
116 
117 static void
bitblt_invert(Byte * src,Byte * dst,int count)118 bitblt_invert( Byte * src, Byte * dst, int count)
119 {
120 	while ( count--) {
121 		*dst = ~(*dst);
122 		dst++;
123 	}
124 }
125 
126 static PBitBltProc
find_blt_proc(int rop)127 find_blt_proc( int rop )
128 {
129 	PBitBltProc proc = NULL;
130 	switch ( rop) {
131 	case ropCopyPut:
132 		proc = bitblt_copy;
133 		break;
134 	case ropAndPut:
135 		proc = bitblt_and;
136 		break;
137 	case ropOrPut:
138 		proc = bitblt_or;
139 		break;
140 	case ropXorPut:
141 		proc = bitblt_xor;
142 		break;
143 	case ropNotPut:
144 		proc = bitblt_not;
145 		break;
146 	case ropNotDestAnd:
147 		proc = bitblt_notdstand;
148 		break;
149 	case ropNotDestOr:
150 		proc = bitblt_notdstor;
151 		break;
152 	case ropNotSrcAnd:
153 		proc = bitblt_notsrcand;
154 		break;
155 	case ropNotSrcOr:
156 		proc = bitblt_notsrcor;
157 		break;
158 	case ropNotXor:
159 		proc = bitblt_notxor;
160 		break;
161 	case ropNotAnd:
162 		proc = bitblt_notand;
163 		break;
164 	case ropNotOr:
165 		proc = bitblt_notor;
166 		break;
167 	case ropBlackness:
168 		proc = bitblt_black;
169 		break;
170 	case ropWhiteness:
171 		proc = bitblt_white;
172 		break;
173 	case ropInvert:
174 		proc = bitblt_invert;
175 		break;
176 	default:
177 		proc = bitblt_copy;
178 	}
179 	return proc;
180 }
181 
182 static Bool
183 img_put_alpha( Handle dest, Handle src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH, int rop, PBoxRegionRec region);
184 
185 static Bool
186 resample_colors( Handle dest, int bpp, PImgPaintContext ctx);
187 
188 typedef struct {
189 	int srcX;
190 	int srcY;
191 	int bpp;
192 	int srcLS;
193 	int dstLS;
194 	int dX;
195 	int dY;
196 	Byte * src;
197 	Byte * dst;
198 	PBitBltProc proc;
199 } ImgPutCallbackRec;
200 
201 static Bool
img_put_single(int x,int y,int w,int h,ImgPutCallbackRec * ptr)202 img_put_single( int x, int y, int w, int h, ImgPutCallbackRec * ptr)
203 {
204 	int i, count;
205 	Byte *dptr, *sptr;
206 	sptr  = ptr->src + ptr->srcLS * (ptr->dY + y) + ptr->bpp * (ptr->dX + x);
207 	dptr  = ptr->dst + ptr->dstLS * y + ptr->bpp * x;
208 	count = w * ptr->bpp;
209 	for ( i = 0; i < h; i++, sptr += ptr->srcLS, dptr += ptr->dstLS)
210 		ptr->proc( sptr, dptr, count);
211 	return true;
212 }
213 
214 Bool
img_put(Handle dest,Handle src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH,int rop,PBoxRegionRec region,Byte * color)215 img_put(
216 	Handle dest, Handle src,
217 	int dstX, int dstY, int srcX, int srcY,
218 	int dstW, int dstH, int srcW, int srcH,
219 	int rop,
220 	PBoxRegionRec region, Byte * color
221 ) {
222 	Point srcSz, dstSz;
223 	int asrcW, asrcH;
224 	Bool newObject = false;
225 
226 	if ( dest == NULL_HANDLE || src == NULL_HANDLE) return false;
227 	if ( rop == ropNoOper) return false;
228 
229 	if ( kind_of( src, CIcon)) {
230 		Image dummy;
231 		PIcon s = PIcon(src);
232 		if ( s-> maskType != imbpp1) {
233 			if ( s-> maskType != imbpp8) croak("panic: bad icon mask type");
234 			return img_put_alpha( dest, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH, rop, region);
235 		}
236 		img_fill_dummy( &dummy, s-> w, s-> h, imBW, s-> mask, stdmono_palette);
237 		img_put( dest, (Handle) &dummy, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH, ropAndPut, region, color);
238 		rop = ropXorPut;
239 	} else if ( rop == ropAlphaCopy ) {
240 		Bool ok;
241 		Image dummy;
242 		PIcon i;
243 		if ( !kind_of( dest, CIcon )) return false;
244 		if ( PImage(src)-> type != imByte ) {
245 			Handle dup = CImage(src)->dup(src);
246 			CImage(dup)->set_type(src, imByte);
247 			ok = img_put( dest, dup, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH, rop, region, color);
248 			Object_destroy(dup);
249 			return ok;
250 		}
251 		if ( PIcon(dest)-> maskType != imbpp8) {
252 			CIcon(dest)-> set_maskType(dest, imbpp8);
253 			ok = img_put( dest, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH, rop, region, color);
254 			if ( PIcon(dest)-> options. optPreserveType )
255 				CIcon(dest)-> set_maskType(dest, imbpp1);
256 			return ok;
257 		}
258 
259 		i = (PIcon) dest;
260 		img_fill_dummy( &dummy, i-> w, i-> h, imByte, i-> mask, NULL);
261 		return img_put((Handle)&dummy, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH, ropCopyPut, region, color);
262 	} else if ( color != NULL ) {
263 		Bool ok;
264 		Icon dummy;
265 		PImage s = PImage(src);
266 		Byte * bits;
267 		ImgPaintContext ctx;
268 		if ( s-> type != imByte ) {
269 			Handle dup = CImage(src)->dup(src);
270 			CImage(dup)->set_type(src, imByte);
271 			ok = img_put( dest, dup, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH, rop, region, color);
272 			Object_destroy(dup);
273 			return ok;
274 		}
275 		bzero( &ctx, sizeof(ctx));
276 		memcpy( ctx.color, color, MAX_SIZEOF_PIXEL);
277 		if ( PImage(dest)-> type & imGrayScale ) {
278 			unsigned size = LINE_SIZE(s->w, imByte) * s->h;
279 			if ( !( bits = malloc( size )))
280 				return false;
281 			resample_colors(dest, imbpp8, &ctx);
282 			memset(bits, ctx.color[0], size );
283 			img_fill_dummy((PImage) &dummy, s-> w, s-> h, imByte, bits, std256gray_palette);
284 		} else {
285 			Byte * line;
286 			int w, linesize = LINE_SIZE(s-> w, imRGB);
287 			if ( !( bits = malloc( linesize * s->h )))
288 				return false;
289 			line = bits;
290 			resample_colors(dest, imbpp24, &ctx);
291 			for ( w = 0; w < s-> w; w++ ) {
292 				*(line++) = ctx.color[0];
293 				*(line++) = ctx.color[1];
294 				*(line++) = ctx.color[2];
295 			}
296 			for ( w = 1, line = bits + linesize; w < s-> h; w++, line += linesize)
297 				memcpy( line, bits, linesize);
298 			img_fill_dummy((PImage) &dummy, s-> w, s-> h, imRGB, bits, NULL);
299 		}
300 		dummy. self     = CIcon;
301 		dummy. mask     = s-> data;
302 		dummy. maskLine = s-> lineSize;
303 		dummy. maskSize = s-> dataSize;
304 		dummy. maskType = imbpp8;
305 		rop &= ~(ropAlphaCopy | ropConstantColor);
306 		ok = img_put(dest, (Handle)&dummy, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH, rop, region, NULL);
307 		free(bits);
308 		return ok;
309 	} else if ( rop & ropConstantAlpha )
310 		return img_put_alpha( dest, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH, rop, region);
311 
312 	srcSz. x = PImage(src)-> w;
313 	srcSz. y = PImage(src)-> h;
314 	dstSz. x = PImage(dest)-> w;
315 	dstSz. y = PImage(dest)-> h;
316 
317 	if ( dstW < 0) {
318 		dstW = abs( dstW);
319 		srcW = -srcW;
320 	}
321 	if ( dstH < 0) {
322 		dstH = abs( dstH);
323 		srcH = -srcH;
324 	}
325 
326 	asrcW = abs( srcW);
327 	asrcH = abs( srcH);
328 
329 	if (
330 		srcX >= srcSz. x || srcX + srcW <= 0 ||
331 		srcY >= srcSz. y || srcY + srcH <= 0 ||
332 		dstX >= dstSz. x || dstX + dstW <= 0 ||
333 		dstY >= dstSz. y || dstY + dstH <= 0
334 	)
335 		return true;
336 
337 	/* check if we can do it without expensive scalings and extractions */
338 	if (
339 		( srcW == dstW) && ( srcH == dstH) &&
340 		( srcX >= 0) && ( srcY >= 0) && ( srcX + srcW <= srcSz. x) && ( srcY + srcH <= srcSz. y)
341 	)
342 		goto NOSCALE;
343 
344 	if ( srcX != 0 || srcY != 0 || asrcW != srcSz. x || asrcH != srcSz. y) {
345 	/* extract source rectangle */
346 		Handle x;
347 		int ssx = srcX, ssy = srcY, ssw = asrcW, ssh = asrcH;
348 		if ( ssx < 0) {
349 			ssw += ssx;
350 			ssx = 0;
351 		}
352 		if ( ssy < 0) {
353 			ssh += ssy;
354 			ssy = 0;
355 		}
356 		x = CImage( src)-> extract( src, ssx, ssy, ssw, ssh);
357 		if ( !x) return false;
358 
359 		if ( srcX < 0 || srcY < 0 || srcX + asrcW >= srcSz. x || srcY + asrcH > srcSz. y) {
360 			HV * profile;
361 			Handle dx;
362 			int dsx = 0, dsy = 0, dsw = PImage(x)-> w, dsh = PImage(x)-> h, type = PImage( dest)-> type;
363 
364 			if ( asrcW != srcW || asrcH != srcH) { /* reverse before application */
365 				CImage( x)-> stretch( x, srcW, srcH);
366 				srcW = asrcW;
367 				srcH = asrcH;
368 				if ( PImage(x)-> w != asrcW || PImage(x)-> h != asrcH) {
369 					Object_destroy( x);
370 					return true;
371 				}
372 			}
373 
374 			if (( type & imBPP) < 8) type = imbpp8;
375 
376 			profile = newHV();
377 			pset_i( type,        type);
378 			pset_i( width,       asrcW);
379 			pset_i( height,      asrcH);
380 			pset_i( conversion,  PImage( src)-> conversion);
381 			dx = Object_create( "Prima::Image", profile);
382 			sv_free((SV*)profile);
383 			if ( !dx) {
384 				Object_destroy( x);
385 				return false;
386 			}
387 			if ( PImage( dx)-> palSize > 0) {
388 				PImage( dx)-> palSize = PImage( x)-> palSize;
389 				memcpy( PImage( dx)-> palette, PImage( x)-> palette, 768);
390 			}
391 			memset( PImage( dx)-> data, 0, PImage( dx)-> dataSize);
392 
393 			if ( srcX < 0) dsx = asrcW - dsw;
394 			if ( srcY < 0) dsy = asrcH - dsh;
395 			img_put( dx, x, dsx, dsy, 0, 0, dsw, dsh, dsw, dsh, ropCopyPut, region, color);
396 			Object_destroy( x);
397 			x = dx;
398 		}
399 
400 		src = x;
401 		newObject = true;
402 		srcX = srcY = 0;
403 	}
404 
405 	if ( srcW != dstW || srcH != dstH) {
406 		/* stretch & reverse */
407 		if ( !newObject) {
408 			src = CImage( src)-> dup( src);
409 			if ( !src) goto EXIT;
410 			newObject = true;
411 		}
412 		if ( srcW != asrcW) {
413 			dstW = -dstW;
414 			srcW = asrcW;
415 		}
416 		if ( srcH != asrcH) {
417 			dstH = -dstH;
418 			srcH = asrcH;
419 		}
420 		CImage(src)-> stretch( src, dstW, dstH);
421 		if ( PImage(src)-> w != dstW || PImage(src)-> h != dstH) goto EXIT;
422 		dstW = abs( dstW);
423 		dstH = abs( dstH);
424 	}
425 
426 NOSCALE:
427 
428 	if (( PImage( dest)-> type & imBPP) < 8) {
429 		PImage i = ( PImage) dest;
430 		int type = i-> type;
431 		if (rop != ropCopyPut || i-> conversion == ictNone) {
432 			Handle b8 = i-> self-> dup( dest);
433 			PImage j  = ( PImage) b8;
434 			int mask  = (1 << (type & imBPP)) - 1;
435 			int sz;
436 			Byte *dj, *di;
437 			Byte colorref[256];
438 			j-> self-> reset( b8, imbpp8, NULL, 0);
439 			sz = j-> dataSize;
440 			dj = j-> data;
441 			/* change 0/1 to 0x000/0xfff for correct masking */
442 			while ( sz--) {
443 				if ( *dj == mask) *dj = 0xff;
444 				dj++;
445 			}
446 			img_put( b8, src, dstX, dstY, 0, 0, dstW, dstH, PImage(src)-> w, PImage(src)-> h, rop, region, color);
447 			for ( sz = 0; sz < 256; sz++) colorref[sz] = ( sz > mask) ? mask : sz;
448 			dj = j-> data;
449 			di = i-> data;
450 
451 			for ( sz = 0; sz < i-> h; sz++, dj += j-> lineSize, di += i-> lineSize) {
452 				if (( type & imBPP) == 1)
453 					bc_byte_mono_cr( dj, di, i-> w, colorref);
454 				else
455 					bc_byte_nibble_cr( dj, di, i-> w, colorref);
456 			}
457 			Object_destroy( b8);
458 		} else {
459 			int conv = i-> conversion;
460 			i-> conversion = PImage( src)-> conversion;
461 			i-> self-> reset( dest, imbpp8, NULL, 0);
462 			img_put( dest, src, dstX, dstY, 0, 0, dstW, dstH, PImage(src)-> w, PImage(src)-> h, rop, region, color);
463 			i-> self-> reset( dest, type, NULL, 0);
464 			i-> conversion = conv;
465 		}
466 		goto EXIT;
467 	}
468 
469 	if ( PImage( dest)-> type != PImage( src)-> type) {
470 		int type = PImage( src)-> type & imBPP;
471 		int mask = (1 << type) - 1;
472 		/* equalize type */
473 		if ( !newObject) {
474 			src = CImage( src)-> dup( src);
475 			if ( !src) goto EXIT;
476 			newObject = true;
477 		}
478 		CImage( src)-> reset( src, PImage( dest)-> type, NULL, 0);
479 		if ( type < 8 && rop != ropCopyPut) {
480 			/* change 0/1 to 0x000/0xfff for correct masking */
481 			int sz   = PImage( src)-> dataSize;
482 			Byte * d = PImage( src)-> data;
483 			while ( sz--) {
484 				if ( *d == mask) *d = 0xff;
485 				d++;
486 			}
487 			memset( PImage( src)-> palette + 255, 0xff, sizeof(RGBColor));
488 		}
489 	}
490 
491 	if ( PImage( dest)-> type == imbpp8) {
492 		/* equalize palette */
493 		Byte colorref[256], *s;
494 		int sz, i = PImage( src)-> dataSize;
495 		if ( !newObject) {
496 			src = CImage( src)-> dup( src);
497 			if ( !src) goto EXIT;
498 			newObject = true;
499 		}
500 		cm_fill_colorref(
501 			PImage( src)-> palette, PImage( src)-> palSize,
502 			PImage( dest)-> palette, PImage( dest)-> palSize,
503 			colorref);
504 		s = PImage( src)-> data;
505 		/* identity transform for padded ( 1->xfff, see above ) pixels */
506 		for ( sz = PImage( src)-> palSize; sz < 256; sz++)
507 			colorref[sz] = sz;
508 		while ( i--) {
509 			*s = colorref[ *s];
510 			s++;
511 		}
512 	}
513 
514 	if ( dstX < 0 || dstY < 0 || dstX + dstW >= dstSz. x || dstY + dstH >= dstSz. y) {
515 		/* adjust destination rectangle */
516 		if ( dstX < 0) {
517 			dstW += dstX;
518 			srcX -= dstX;
519 			dstX = 0;
520 		}
521 		if ( dstY < 0) {
522 			dstH += dstY;
523 			srcY -= dstY;
524 			dstY = 0;
525 		}
526 		if ( dstX + dstW > dstSz. x)
527 			dstW = dstSz. x - dstX;
528 		if ( dstY + dstH > dstSz. y)
529 			dstH = dstSz. y - dstY;
530 	}
531 
532 	/* checks done, do put_image */
533 	{
534 		ImgPutCallbackRec rec = {
535 			/* srcX  */ srcX,
536 			/* srcY  */ srcY,
537 			/* bpp   */ ( PImage( dest)-> type & imBPP ) / 8,
538 			/* srcLS */ PImage( src)-> lineSize,
539 			/* dstLS */ PImage( dest)-> lineSize,
540 			/* dX    */ srcX - dstX,
541 			/* dY    */ srcY - dstY,
542 			/* src   */ PImage( src )-> data,
543 			/* dst   */ PImage( dest )-> data,
544 			/* proc  */ find_blt_proc(rop)
545 		};
546 		if ( rec.proc == bitblt_copy && dest == src) /* incredible */
547 			rec.proc = bitblt_move;
548 		img_region_foreach( region,
549 			dstX, dstY, dstW, dstH,
550 			(RegionCallbackFunc*)img_put_single, &rec
551 		);
552 	}
553 
554 EXIT:
555 	if ( newObject) Object_destroy( src);
556 
557 	return true;
558 }
559 
560 typedef struct {
561 	int bpp;
562 	int count;
563 	int ls;
564 	int step;
565 	int pat_x_offset;
566 	Bool solid;
567 	Byte * data;
568 	Byte * buf;
569 	PBitBltProc proc;
570 } ImgBarCallbackRec;
571 
572 #define FILL_PATTERN_SIZE 8
573 #define BLT_BUFSIZE ((MAX_SIZEOF_PIXEL * FILL_PATTERN_SIZE * FILL_PATTERN_SIZE) * 2)
574 
575 static Bool
img_bar_single(int x,int y,int w,int h,ImgBarCallbackRec * ptr)576 img_bar_single( int x, int y, int w, int h, ImgBarCallbackRec * ptr)
577 {
578 	int j, blt_bytes, blt_step, offset;
579 	Byte lmask, rmask;
580 	Byte * data, *pat_ptr;
581 
582 	switch ( ptr->bpp ) {
583 	case 1:
584 		blt_bytes = (( x + w - 1) >> 3) - (x >> 3) + 1;
585 		lmask = ( x & 7 ) ? 255 << ( 8 - (x & 7)) : 0;
586 		rmask = (( x + w) & 7 ) ? 255 >> ((x + w) & 7) : 0;
587 		offset = x >> 3;
588 		break;
589 	case 4:
590 		blt_bytes = (( x + w - 1) >> 1) - (x >> 1) + 1;
591 		lmask = ( x & 1 )       ? 0xf0 : 0;
592 		rmask = (( x + w) & 1 ) ? 0x0f : 0;
593 		offset = x >> 1;
594 		break;
595 	case 8:
596 		blt_bytes = w;
597 		lmask = rmask = 0;
598 		offset = x;
599 		break;
600 	default:
601 		blt_bytes = w * ptr->count;
602 		lmask = rmask = 0;
603 		offset = x * ptr->count;
604 	}
605 
606 	blt_step = ptr->step;
607 	if (!ptr->solid && (( ptr-> pat_x_offset % FILL_PATTERN_SIZE ) != (x % FILL_PATTERN_SIZE))) {
608 		int dx = (x % FILL_PATTERN_SIZE) - ( ptr-> pat_x_offset % FILL_PATTERN_SIZE );
609 		if ( dx < 0 ) dx += FILL_PATTERN_SIZE;
610 
611 		switch ( ptr->bpp ) {
612 		case 1:
613 			pat_ptr = ptr->buf;
614 			break;
615 		case 4:
616 			if ( dx > 1 ) {
617 				pat_ptr = ptr->buf + dx / 2;
618 				if ( dx > 0 || blt_step + FILL_PATTERN_SIZE / 2 > BLT_BUFSIZE )
619 					blt_step -= FILL_PATTERN_SIZE / 2;
620 			} else
621 				pat_ptr = ptr->buf;
622 			break;
623 		default:
624 			pat_ptr = ptr->buf + dx * ptr->bpp / 8;
625 			if ( dx > 0 || blt_step + FILL_PATTERN_SIZE * ptr->count > BLT_BUFSIZE )
626 				blt_step -= FILL_PATTERN_SIZE * ptr->count;
627 		}
628 	} else {
629 		pat_ptr = ptr->buf;
630 	}
631 
632 	if (blt_bytes < blt_step) blt_step = blt_bytes;
633 
634 	data = ptr->data + ptr->ls * y + offset;
635 
636 	for ( j = 0; j < h; j++) {
637 		int bytes = blt_bytes;
638 		Byte lsave = *data, rsave = data[blt_bytes - 1], *p = data;
639 		Byte * src = pat_ptr + ((y + j) % FILL_PATTERN_SIZE) * ptr->step;
640 		while ( bytes > 0 ) {
641 			ptr->proc( src, p, ( bytes > blt_step ) ? blt_step : bytes );
642 			bytes -= blt_step;
643 			p += blt_step;
644 		}
645 		if ( lmask ) *data = (lsave & lmask) | (*data & ~lmask);
646 		if ( rmask ) data[blt_bytes-1] = (rsave & rmask) | (data[blt_bytes-1] & ~rmask);
647 		data += ptr->ls;
648 	}
649 	return true;
650 }
651 
652 static Bool
653 img_bar_alpha( Handle dest, int x, int y, int w, int h, PImgPaintContext ctx);
654 
655 Bool
img_bar(Handle dest,int x,int y,int w,int h,PImgPaintContext ctx)656 img_bar( Handle dest, int x, int y, int w, int h, PImgPaintContext ctx)
657 {
658 	PImage i     = (PImage) dest;
659 	int pixSize  = (i->type & imBPP) / 8;
660 	Byte blt_buffer[BLT_BUFSIZE];
661 	int j, k, blt_bytes, blt_step;
662 	Bool solid;
663 
664 	/* check boundaries */
665 	if ( ctx->rop == ropNoOper) return true;
666 
667 	if ( x < 0 ) {
668 		w += x;
669 		x = 0;
670 	}
671 	if ( y < 0 ) {
672 		h += y;
673 		y = 0;
674 	}
675 	if ( x + w > i->w ) w = i->w - x;
676 	if ( y + h > i->h ) h = i->h - y;
677 	if ( w <= 0 || h <= 0 ) return true;
678 
679 	while ( ctx->patternOffset.x < 0 ) ctx-> patternOffset.x += FILL_PATTERN_SIZE;
680 	while ( ctx->patternOffset.y < 0 ) ctx-> patternOffset.y += FILL_PATTERN_SIZE;
681 
682 
683 	if ( ctx-> rop & ropConstantAlpha )
684 		return img_bar_alpha(dest, x, y, w, h, ctx);
685 
686 	if ( memcmp( ctx->pattern, fillPatterns[fpSolid], sizeof(FillPattern)) == 0) {
687 		/* do nothing */
688 	} else if (memcmp( ctx->pattern, fillPatterns[fpEmpty], sizeof(FillPattern)) == 0) {
689 		if ( ctx->transparent ) return true;
690 		/* still do nothing */
691 	} else if ( ctx->transparent ) {
692 	/* transparent stippling: if rop is simple enough, adjust parameters to
693 	execute it as another rop with adjusted input. Otherwise make it into
694 	two-step operation, such as CopyPut stippling is famously executed by
695 	And and Xor rops */
696 		#define FILL(who,val) memset( ctx->who, val, MAX_SIZEOF_PIXEL)
697 		switch ( ctx-> rop ) {
698 		case ropBlackness:
699 			FILL(color,0x00);
700 			FILL(backColor,0xff);
701 			ctx->rop = ropAndPut;
702 			break;
703 		case ropWhiteness:
704 			FILL(color,0xff);
705 			FILL(backColor,0x00);
706 			ctx->rop = ropOrPut;
707 			break;
708 		case ropInvert:
709 			FILL(color,0xff);
710 			FILL(backColor,0x00);
711 			ctx->rop = ropXorPut;
712 			break;
713 		case ropNotSrcAnd:
714 		case ropXorPut:
715 			FILL(backColor,0x00);
716 			break;
717 		default: {
718 			static int rop1[16] = {
719 				ropNotOr, ropXorPut, ropInvert, ropNotOr,
720 				ropNotSrcAnd, ropXorPut, ropNotSrcAnd, ropXorPut,
721 				ropNotOr, ropNotOr, ropNotSrcAnd, ropInvert,
722 				ropInvert, ropXorPut, ropNotSrcAnd, ropInvert
723 			};
724 			static int rop2[16] = {
725 				ropNotDestAnd, ropNoOper, ropNotDestAnd, ropInvert,
726 				ropNotSrcOr, ropNotXor, ropAndPut, ropAndPut,
727 				ropXorPut, ropNotAnd, ropNoOper, ropNotAnd,
728 				ropXorPut, ropNotSrcOr, ropNotXor, ropInvert
729 			};
730 			int rop = ctx->rop;
731 			FILL(backColor,0x00);
732 			ctx->rop = rop1[rop];
733 			ctx->transparent = false;
734 			img_bar( dest, x, y, w, h, ctx);
735 			FILL(backColor,0xff);
736 			ctx->rop = rop2[rop];
737 			break;
738 		}}
739 	}
740 
741 	/* render a 8x8xPIXEL matrix with pattern, then horizontally
742 	replicate it over blt_buffer as much as possible, to streamline
743 	byte operations */
744 	switch ( i->type & imBPP) {
745 	case imbpp1:
746 		 blt_bytes = (( x + w - 1) >> 3) - (x >> 3) + 1;
747 		if ( blt_bytes < FILL_PATTERN_SIZE ) blt_bytes = FILL_PATTERN_SIZE;
748 		break;
749 	case imbpp4:
750 		blt_bytes = (( x + w - 1) >> 1) - (x >> 1) + 1;
751 		if ( blt_bytes < FILL_PATTERN_SIZE / 2 ) blt_bytes = FILL_PATTERN_SIZE / 2;
752 		break;
753 	default:
754 		blt_bytes = w * pixSize;
755 		if ( blt_bytes < FILL_PATTERN_SIZE * pixSize ) blt_bytes = FILL_PATTERN_SIZE * pixSize;
756 	}
757 	blt_bytes *= FILL_PATTERN_SIZE;
758 	blt_step = ((blt_bytes > BLT_BUFSIZE) ? BLT_BUFSIZE : blt_bytes) / FILL_PATTERN_SIZE;
759 	if ( pixSize > 1 )
760 		blt_step = (blt_step / pixSize / FILL_PATTERN_SIZE) * pixSize * FILL_PATTERN_SIZE;
761 	solid = (memcmp( ctx->pattern, fillPatterns[fpSolid], sizeof(FillPattern)) == 0);
762 	for ( j = 0; j < FILL_PATTERN_SIZE; j++) {
763 		unsigned int pat, strip_size;
764 		Byte matrix[MAX_SIZEOF_PIXEL * FILL_PATTERN_SIZE], *buffer;
765 		if ( solid ) {
766 			pat = 0xff;
767 		} else {
768 			pat = (unsigned int) ctx->pattern[(j + ctx->patternOffset. y) % FILL_PATTERN_SIZE];
769 			pat = (((pat << 8) | pat) >> ((ctx->patternOffset. x + 8 - (x % 8)) % FILL_PATTERN_SIZE)) & 0xff;
770 		}
771 		buffer = blt_buffer + j * blt_step;
772 		switch ( i->type & imBPP) {
773 		case 1:
774 			strip_size = 1;
775 			matrix[0] = ctx->color[0] ?
776 				(ctx->backColor[0] ? 0xff : pat) :
777 				(ctx->backColor[0] ? ~pat : 0);
778 			memset( buffer, matrix[0], blt_step);
779 			break;
780 		case 4:
781 			strip_size = FILL_PATTERN_SIZE / 2;
782 			for ( k = 0; k < FILL_PATTERN_SIZE; ) {
783 				Byte c1 = *((pat & (0x80 >> k++)) ? ctx->color : ctx->backColor);
784 				Byte c2 = *((pat & (0x80 >> k++)) ? ctx->color : ctx->backColor);
785 				matrix[ (k / 2) - 1] = (c1 << 4) | (c2 & 0xf);
786 			}
787 			break;
788 		case 8:
789 			strip_size = FILL_PATTERN_SIZE;
790 			for ( k = 0; k < FILL_PATTERN_SIZE; k++)
791 				matrix[k] = *((pat & (0x80 >> k)) ? ctx->color : ctx->backColor);
792 			break;
793 		default:
794 			strip_size = FILL_PATTERN_SIZE * pixSize;
795 			for ( k = 0; k < FILL_PATTERN_SIZE; k++) {
796 				Byte * color = (pat & (0x80 >> k)) ? ctx->color : ctx->backColor;
797 				memcpy( matrix + k * pixSize, color, pixSize);
798 			}
799 		}
800 		if ( strip_size > 1 ) {
801 			Byte * buf = buffer;
802 			for ( k = 0; k < blt_step / strip_size; k++, buf += strip_size)
803 				memcpy( buf, matrix, strip_size);
804 			if ( blt_step % strip_size != 0)
805 				memcpy( buf, matrix, blt_step % strip_size);
806 		}
807 	}
808 	/*
809 	printf("pxs:%d step:%d\n", pixSize, blt_step);
810 	for ( j = 0; j < 8; j++) {
811 		printf("%d: ", j);
812 		for ( k = 0; k < blt_step; k++) {
813 			printf("%02x", blt_buffer[ j * blt_step + k]);
814 		}
815 		printf("\n");
816 	} */
817 	{
818 		ImgBarCallbackRec rec = {
819 			/* bpp          */ (i->type & imBPP),
820 			/* count        */ (i->type & imBPP) / 8,
821 			/* ls           */ i->lineSize,
822 			/* step         */ blt_step,
823 			/* pat_x_offset */ x,
824 			/* solid        */ solid,
825 			/* data         */ i->data,
826 			/* buf          */ blt_buffer,
827 			/* proc         */ find_blt_proc(ctx->rop),
828 		};
829 		img_region_foreach( ctx->region,
830 			x, y, w, h,
831 			(RegionCallbackFunc*)img_bar_single, &rec
832 		);
833 	}
834 
835 	return true;
836 }
837 
838 /* reformat color values to imByte/imRGB */
839 static Bool
resample_colors(Handle dest,int bpp,PImgPaintContext ctx)840 resample_colors( Handle dest, int bpp, PImgPaintContext ctx)
841 {
842 	RGBColor fg, bg;
843 	int type = PImage(dest)->type;
844 	int rbpp = type & imBPP;
845 	if (rbpp <= 8 ) {
846 		fg = PImage(dest)->palette[*(ctx->color)];
847 		bg = PImage(dest)->palette[*(ctx->backColor)];
848 	} else switch ( type ) {
849 	case imRGB:
850 		fg.b = ctx->color[0];
851 		fg.g = ctx->color[1];
852 		fg.r = ctx->color[2];
853 		bg.b = ctx->backColor[0];
854 		bg.g = ctx->backColor[1];
855 		bg.r = ctx->backColor[2];
856 		break;
857 	case imShort:
858 		fg.b = fg.g = fg.r = *((Short*)(ctx->color));
859 		bg.b = bg.g = bg.r = *((Short*)(ctx->backColor));
860 		break;
861 	case imLong:
862 		fg.b = fg.g = fg.r = *((Long*)(ctx->color));
863 		bg.b = bg.g = bg.r = *((Long*)(ctx->backColor));
864 		break;
865 	case imFloat: case imComplex: case imTrigComplex:
866 		fg.b = fg.g = fg.r = *((float*)(ctx->color));
867 		bg.b = bg.g = bg.r = *((float*)(ctx->backColor));
868 		break;
869 	case imDouble: case imDComplex: case imTrigDComplex:
870 		fg.b = fg.g = fg.r = *((double*)(ctx->color));
871 		bg.b = bg.g = bg.r = *((double*)(ctx->backColor));
872 		break;
873 	default:
874 		return false;
875 	}
876 	if ( bpp == imByte ) {
877 		*(ctx->color)     = (fg.r + fg.g + fg.b) / 3;
878 		*(ctx->backColor) = (bg.r + bg.g + bg.b) / 3;
879 	} else {
880                 ctx->color[0] = fg.b;
881                 ctx->color[1] = fg.g;
882                 ctx->color[2] = fg.r;
883                 ctx->backColor[0] = bg.b;
884                 ctx->backColor[1] = bg.g;
885                 ctx->backColor[2] = bg.r;
886 	}
887 	return true;
888 }
889 
890 #define VISIBILITY_NONE       0
891 #define VISIBILITY_CLIPPED    1
892 #define VISIBILITY_UNSURE     2
893 #define VISIBILITY_CLEAR      3
894 
895 static void
fill_alpha_buf(Byte * dst,Byte * src,int width,int bpp)896 fill_alpha_buf( Byte * dst, Byte * src, int width, int bpp)
897 {
898 	register int x = width;
899 	if ( bpp == 3 ) {
900 		while (x-- > 0) {
901 			register Byte a = *src++;
902 			*dst++ = a;
903 			*dst++ = a;
904 			*dst++ = a;
905 		}
906 	} else
907 		memcpy( dst, src, width * bpp);
908 }
909 
910 #define dBLEND_FUNC(name) void name( \
911 	const Byte * src, const Byte src_inc, \
912 	const Byte * src_a, const Byte src_a_inc,\
913 	Byte * dst, \
914 	const Byte * dst_a, const Byte dst_a_inc,\
915 	int bytes)
916 
917 #define dVAL(x) register int32_t s = x
918 #define STORE \
919 	*dst++ = ( s > 255 ) ? 255 : s;\
920 	src += src_inc;\
921 	src_a += src_a_inc;\
922 	dst_a += dst_a_inc
923 #define BLEND_LOOP while(bytes-- > 0)
924 
925 #define UP(x) ((int32_t)(x) << 8 )
926 #define DOWN(expr) (((expr) + 127) >> 8)
927 
928 #define dBLEND_FUNCx(name,expr) \
929 static dBLEND_FUNC(name) \
930 { \
931 	BLEND_LOOP {\
932 		dVAL(DOWN(expr));\
933 		STORE;\
934 	}\
935 }
936 
937 typedef dBLEND_FUNC(BlendFunc);
938 
939 #define S (*src)
940 #define D (*dst)
941 #define SA (*src_a)
942 #define DA (*dst_a)
943 #define INVSA (255 - *src_a)
944 #define INVDA (255 - *dst_a)
945 
946 /* sss */
dBLEND_FUNC(blend_src_copy)947 static dBLEND_FUNC(blend_src_copy)
948 {
949 	if ( src_inc )
950 		memcpy( dst, src, bytes);
951 	else
952 		memset( dst, *src, bytes);
953 }
954 
955 /* ddd */
dBLEND_FUNC(blend_dst_copy)956 static dBLEND_FUNC(blend_dst_copy)
957 {
958 }
959 
960 /* 0 */
dBLEND_FUNC(blend_clear)961 static dBLEND_FUNC(blend_clear)
962 {
963 	memset( dst, 0, bytes);
964 }
965 
966 dBLEND_FUNCx(blend_src_over,  UP(S) + UP(D) * INVSA / 255)
967 dBLEND_FUNCx(blend_xor,      (UP(S) * INVDA + UP(D) * INVSA) / 255)
968 dBLEND_FUNCx(blend_dst_over,  UP(D) + UP(S) * INVDA / 255)
969 dBLEND_FUNCx(blend_src_in,    UP(S) * DA / 255)
970 dBLEND_FUNCx(blend_dst_in,    UP(D) * SA / 255)
971 dBLEND_FUNCx(blend_src_out,   UP(S) * INVDA / 255)
972 dBLEND_FUNCx(blend_dst_out,   UP(D) * INVSA / 255)
973 dBLEND_FUNCx(blend_src_atop, (UP(S) * DA + UP(D) * INVSA) / 255)
974 dBLEND_FUNCx(blend_dst_atop, (UP(D) * SA + UP(S) * INVDA) / 255)
975 
976 /* sss + ddd */
dBLEND_FUNC(blend_add)977 static dBLEND_FUNC(blend_add)
978 {
979 	BLEND_LOOP {
980 		dVAL(S + D);
981 		STORE;
982 	}
983 }
984 
985 /* SEPARABLE(S * D) */
986 dBLEND_FUNCx(blend_multiply, (UP(D) * (S + INVSA) + UP(S) * INVDA) / 255)
987 
988 /* SEPARABLE(D * SA + S * DA - S * D) */
989 dBLEND_FUNCx(blend_screen,   (UP(S) * 255 + UP(D) * (255 - S)) / 255)
990 
991 #define SEPARABLE(f) (UP(S) * INVDA + UP(D) * INVSA + (f))/255
992 dBLEND_FUNCx(blend_overlay, SEPARABLE(
993 	(2 * D < DA) ?
994 		(2 * UP(D) * S) :
995 		(UP(SA) * DA - UP(2) * (DA - D) * (SA - S))
996 	))
997 
dBLEND_FUNC(blend_darken)998 static dBLEND_FUNC(blend_darken)
999 {
1000 	BLEND_LOOP {
1001 		register int32_t ss = UP(S) * DA;
1002 		register int32_t dd = UP(D) * SA;
1003 		dVAL(DOWN(SEPARABLE((ss > dd) ? dd : ss)));
1004 		STORE;
1005 	}
1006 }
1007 
dBLEND_FUNC(blend_lighten)1008 static dBLEND_FUNC(blend_lighten)
1009 {
1010 	BLEND_LOOP {
1011 		register int32_t ss = UP(S) * DA;
1012 		register int32_t dd = UP(D) * SA;
1013 		dVAL(DOWN(SEPARABLE((ss > dd) ? ss : dd)));
1014 		STORE;
1015 	}
1016 }
1017 
dBLEND_FUNC(blend_color_dodge)1018 static dBLEND_FUNC(blend_color_dodge)
1019 {
1020 	BLEND_LOOP {
1021 		register int32_t s;
1022 		if ( S >= SA ) {
1023 			s = D ? UP(SA) * DA : 0;
1024 		} else {
1025 			register int32_t dodge = D * SA / (SA - S);
1026 			s = UP(SA) * ((DA < dodge) ? DA : dodge);
1027 		}
1028 		s = DOWN(SEPARABLE(s));
1029 		STORE;
1030 	}
1031 }
1032 
dBLEND_FUNC(blend_color_burn)1033 static dBLEND_FUNC(blend_color_burn)
1034 {
1035 	BLEND_LOOP {
1036 		register int32_t s;
1037 		if ( S == 0 ) {
1038 			s = (D < DA) ? 0 : UP(SA) * DA;
1039 		} else {
1040 			register int32_t burn = (DA - D) * SA / S;
1041 			s = (DA < burn) ? 0 : UP(SA) * (DA - burn);
1042 		}
1043 		s = DOWN(SEPARABLE(s));
1044 		STORE;
1045 	}
1046 }
1047 
1048 dBLEND_FUNCx(blend_hard_light, SEPARABLE(
1049 	(2 * S < SA) ?
1050 		(2 * UP(D) * S) :
1051 		(UP(SA) * DA - UP(2) * (DA - D) * (SA - S))
1052 	))
1053 
dBLEND_FUNC(blend_soft_light)1054 static dBLEND_FUNC(blend_soft_light)
1055 {
1056 	BLEND_LOOP {
1057 		register int32_t s;
1058 		if ( 2 * S < SA ) {
1059 			s = DA ? D * (UP(SA) - UP(DA - D) * (SA - 2 * S) / DA ) : 0;
1060 		} else if (DA == 0) {
1061 			s = 0;
1062 		} else if (4 * D <= DA) {
1063 			s = D * (UP(SA) + (2 * S - SA) * ((UP(16) * D / DA - UP(12)) * D / DA + UP(3)));
1064 		} else {
1065 			s = 256 * (D * SA + (sqrt(D * DA) - D) * (2 * SA - S));
1066 		}
1067 		s = DOWN(SEPARABLE(s));
1068 		STORE;
1069 	}
1070 }
1071 
dBLEND_FUNC(blend_difference)1072 static dBLEND_FUNC(blend_difference)
1073 {
1074 	BLEND_LOOP {
1075 		dVAL(UP(D) * SA - UP(S) * DA);
1076 		if ( s < 0 ) s = -s;
1077 		s = DOWN(SEPARABLE(s));
1078 		STORE;
1079 	}
1080 }
1081 
1082 dBLEND_FUNCx(blend_exclusion, SEPARABLE( UP(S) * (DA - 2 * D) + UP(D) * SA ))
1083 
1084 static BlendFunc* blend_functions[] = {
1085 	blend_src_over,
1086 	blend_xor,
1087 	blend_dst_over,
1088 	blend_src_copy,
1089 	blend_dst_copy,
1090 	blend_clear,
1091 	blend_src_in,
1092 	blend_dst_in,
1093 	blend_src_out,
1094 	blend_dst_out,
1095 	blend_src_atop,
1096 	blend_dst_atop,
1097 	blend_add,
1098 	blend_multiply,
1099 	blend_screen,
1100 	blend_dst_copy,
1101 	blend_overlay,
1102 	blend_darken,
1103 	blend_lighten,
1104 	blend_color_dodge,
1105 	blend_color_burn,
1106 	blend_hard_light,
1107 	blend_soft_light,
1108 	blend_difference,
1109 	blend_exclusion
1110 };
1111 
1112 static void
find_blend_proc(int rop,BlendFunc ** blend1,BlendFunc ** blend2)1113 find_blend_proc( int rop, BlendFunc ** blend1, BlendFunc ** blend2 )
1114 {
1115 	*blend1 = blend_functions[rop];
1116 	*blend2 = (rop >= ropMultiply) ? blend_functions[ropScreen] : blend_functions[rop];
1117 }
1118 
1119 typedef struct {
1120 	PIcon       i;
1121 	PBitBltProc proc;
1122 	BlendFunc  *blend1, *blend2;
1123 	int         bpp, bytes, optimized_stride;
1124 	PImgPaintContext ctx;
1125 	Byte        *color;
1126 
1127 	Bool        use_dst_alpha, is_icon;
1128 	Byte        src_alpha,dst_alpha;
1129 } ImgHLineRec;
1130 
1131 static void
setpixel(ImgHLineRec * rec,int x,int y)1132 setpixel( ImgHLineRec* rec, int x, int y)
1133 {
1134 	switch ( rec->bpp ) {
1135 	case 1: {
1136 		Byte * dst = rec->i->data + rec->i->lineSize * y + x / 8, src = *dst;
1137 		Byte shift = 7 - (x & 7);
1138 		src = (src >> shift) & 1;
1139 		rec->proc( rec->color, &src, 1);
1140 		if ( src & 1 )
1141 			*dst |= 1 << shift;
1142 		else
1143 			*dst &= ~(1 << shift);
1144 		break;
1145 	}
1146 	case 4: {
1147 		Byte * dst = rec->i->data + rec->i->lineSize * y + x / 2, src = *dst, tmp = *dst;
1148 		if ( x & 1 ) {
1149 			rec->proc( rec->color, &src, 1);
1150 			*dst = (tmp & 0xf0) | (src & 0x0f);
1151 		} else {
1152 			src >>= 4;
1153 			rec->proc( rec->color, &src, 1);
1154 			*dst = (tmp & 0x0f) | (src << 4);
1155 		}
1156 		break;
1157 	}
1158 	case 8:
1159 		if ( rec->proc)
1160 			rec->proc( rec->color, rec->i->data + rec->i->lineSize * y + x, 1);
1161 		else
1162 			rec->blend1(
1163 				rec-> color, 0, &rec->src_alpha, 0,
1164 				rec->i->data + rec->i->lineSize * y + x,
1165 				rec->use_dst_alpha ?
1166 					&rec->dst_alpha : (rec->i->mask + rec->i->maskLine * y + x), 0,
1167 				1
1168 			);
1169 		break;
1170 	default:
1171 		if ( rec->proc)
1172 			rec->proc( rec->color, rec->i->data + rec->i->lineSize * y + x * rec->bytes, rec->bytes);
1173 		else
1174 			rec->blend1(
1175 				rec-> color, 0, &rec->src_alpha, 0,
1176 				rec->i->data + rec->i->lineSize * y + x * rec->bytes,
1177 				rec->use_dst_alpha ?
1178 					&rec->dst_alpha : (rec->i->mask + rec->i->maskLine * y + x), 0,
1179 				rec->bytes
1180 			);
1181 	}
1182 
1183 	if ( rec->blend2 && rec->is_icon ) {
1184 		Byte * a = rec->i->mask + rec->i->maskLine * y + x;
1185 		rec->blend2( &rec->src_alpha, 0, &rec->src_alpha, 0, (Byte*)a, a, 0, 1);
1186 	}
1187 }
1188 
1189 static void
hline(ImgHLineRec * rec,int x,int n,int y)1190 hline( ImgHLineRec *rec, int x, int n, int y)
1191 {
1192 	switch ( rec->bpp) {
1193 	case 8:
1194 	case 24: {
1195 		/* optimized multipixel set */
1196 		int wn;
1197 		int w = rec->bytes, stride = rec->optimized_stride;
1198 		Byte * dst = rec->i->data + rec->i->lineSize * y + x * w;
1199 		Byte * mask = ( rec->blend1 && !rec->use_dst_alpha) ?
1200 			(rec->i->mask + rec->i->maskLine * y + x) : NULL;
1201 		for ( wn = w * n; wn > 0; wn -= stride, dst += stride) {
1202 			int dw = ( wn >= stride ) ? stride : wn;
1203 			if ( rec->proc )
1204 				rec->proc( rec->color, dst, dw);
1205 			else {
1206 				Byte mask_buf[MAX_SIZEOF_PIXEL];
1207 				if ( mask ) {
1208 					int bp = rec->bpp / 8;
1209 					int dm = dw / bp;
1210 					fill_alpha_buf( mask_buf, mask, dm, bp);
1211 					rec->blend2(
1212 						&rec->src_alpha, 0,
1213 						&rec->src_alpha, 0,
1214 						mask, mask, 1, dm);
1215 					mask += dm;
1216 				}
1217 				rec->blend1( rec->color, 1,
1218 					&rec->src_alpha, 0,
1219 					dst,
1220 					mask ? mask_buf : &rec->dst_alpha,
1221 					rec->use_dst_alpha ? 0 : 1,
1222 					dw);
1223 			}
1224 		}
1225 		return;
1226 	}
1227 	default: {
1228 		int i;
1229 		for ( i = 0; i < n; i++, x++) setpixel(rec, x, y);
1230 	}}
1231 }
1232 
1233 #define HLINE_INIT_OK    0
1234 #define HLINE_INIT_FAIL  1
1235 #define HLINE_INIT_RETRY 2
1236 
1237 /* prepare to draw horizontal lines with alpha etc using single solid color */
1238 static int
hline_init(ImgHLineRec * rec,Handle dest,PImgPaintContext ctx,char * method)1239 hline_init( ImgHLineRec * rec, Handle dest, PImgPaintContext ctx, char * method)
1240 {
1241 	int i;
1242 
1243 	/* misc */
1244 	rec->ctx     = ctx;
1245 	rec->i       = (PIcon) dest;
1246 	rec->bpp     = rec->i->type & imBPP;
1247 	rec->bytes   = rec->bpp / 8;
1248 
1249 	/* deal with alpha request */
1250 	if ( ctx-> rop & ropConstantAlpha ) {
1251 		int j, rop = ctx->rop;
1252 		/* differentiate between per-pixel alpha and a global value */
1253 		if ( ctx->rop & ropSrcAlpha )
1254 			rec->src_alpha = (rop >> ropSrcAlphaShift) & 0xff;
1255 		else
1256 			rec->src_alpha = 0xff;
1257 		if ( rop & ropDstAlpha ) {
1258 			rec->use_dst_alpha = true;
1259 			rec->dst_alpha = (rop >> ropDstAlphaShift) & 0xff;
1260 		}
1261 		rop &= ropPorterDuffMask;
1262 		if ( rop > ropMaxPDFunc || rop < 0 ) return false;
1263 		find_blend_proc( rop, &rec->blend1, &rec->blend2 );
1264 		rec->is_icon = kind_of( dest, CIcon );
1265 
1266 		/* align types and geometry - can only operate over imByte and imRGB */
1267 		int bpp = ( PImage(dest)->type & imGrayScale) ? imByte : imRGB;
1268 		if (PImage(dest)-> type != bpp || ( rec->is_icon && PIcon(dest)->maskType != imbpp8 )) {
1269 			int type = PImage(dest)->type;
1270 			int mask = rec->is_icon ? PIcon(dest)->maskType : 0;
1271 
1272 			if ( type != bpp ) {
1273 				resample_colors( dest, bpp, ctx );
1274 				CIcon(dest)-> set_type( dest, bpp );
1275 				if ( PImage(dest)->type != bpp)
1276 					return HLINE_INIT_FAIL;
1277 			}
1278 			if ( rec->is_icon && mask != imbpp8 ) {
1279 				CIcon(dest)-> set_maskType( dest, imbpp8 );
1280 				if ( PIcon(dest)->maskType != imbpp8)
1281 					return HLINE_INIT_FAIL;
1282 			}
1283 			return HLINE_INIT_RETRY;
1284 		}
1285 
1286 		if ( rec->is_icon ) {
1287 			if ( PIcon(dest)-> maskType != imbpp8)
1288 				croak("panic: assert failed for %s: %s", method, "dst mask type");
1289 			rec->use_dst_alpha = false;
1290 		} else if ( !rec->use_dst_alpha ) {
1291 			rec->use_dst_alpha = true;
1292 			rec->dst_alpha = 0xff;
1293 		}
1294 		rec->proc = NULL;
1295 
1296 		/* premultiply colors */
1297 		for ( j = 0; j < bpp / 8; j++) {
1298 			ctx->color[j] = (float)(ctx->color[j] * rec->src_alpha) / 255.0 + .5;
1299 			ctx->backColor[j] = (float)(ctx->backColor[j] * rec->src_alpha) / 255.0 + .5;
1300 		}
1301 	} else {
1302 		rec->blend1 = rec->blend2 = NULL;
1303 		rec->proc = find_blt_proc(ctx->rop);
1304 	}
1305 
1306 	rec->color   = ctx->color;
1307 
1308 	/* colors; optimize 8 and 24 pixels for horizontal line memcpy */
1309 	switch ( rec->bpp ) {
1310 	case 8:
1311 		memset( ctx->color + 1, ctx->color[0], MAX_SIZEOF_PIXEL - 1);
1312 		rec->optimized_stride = MAX_SIZEOF_PIXEL;
1313 		break;
1314 	case 24:
1315 		for ( i = 1; i < MAX_SIZEOF_PIXEL / 3; i++)
1316 			memcpy( ctx->color + i * 3, ctx->color, 3);
1317 		rec->optimized_stride = (MAX_SIZEOF_PIXEL / 3) * 3;
1318 	}
1319 
1320 	return HLINE_INIT_OK;
1321 }
1322 
1323 typedef struct {
1324 	ImgHLineRec h;
1325 	Bool        solid, segment_is_fg, skip_pixel;
1326 	int         current_segment, segment_offset, n_segments;
1327 } ImgSegmentedLineRec;
1328 
1329 static void
segmented_hline(ImgSegmentedLineRec * rec,int x1,int x2,int y,int visibility)1330 segmented_hline( ImgSegmentedLineRec *rec, int x1, int x2, int y, int visibility)
1331 {
1332 	int n  = abs(x2 - x1) + 1;
1333 	int dx = (x1 < x2) ? 1 : -1;
1334 	/* printf("(%d,%d)->%d %d\n", x1, y, x2,visibility); */
1335 	if ( rec->skip_pixel ) {
1336 		rec->skip_pixel = false;
1337 		if ( n-- == 1 ) return;
1338 		x1 += dx;
1339 	}
1340 	if ( rec->solid) {
1341 		if ( visibility == VISIBILITY_CLEAR ) {
1342 			if ( x2 < x1 )
1343 				hline( &rec->h, x2, x1 - x2 + 1, y);
1344 			else
1345 				hline( &rec->h, x1, x2 - x1 + 1, y);
1346 		} else {
1347 			/* VISIBILITY_NONE is not reaching here */
1348 			int i;
1349 			for ( i = 0; i < n; i++, x1 += dx)
1350 				if ( img_point_in_region(x1, y, rec->h.ctx->region))
1351 					setpixel(&rec->h, x1, y);
1352 		}
1353 	} else {
1354 		int i;
1355 		for ( i = 0; i < n; i++, x1 += dx) {
1356 			/* calculate color */
1357 			rec->h.color = rec->segment_is_fg ?
1358 				rec->h.ctx->color :
1359 				( rec->h.ctx->transparent ? NULL : rec->h.ctx->backColor )
1360 				;
1361 			if ( ++rec->segment_offset >= rec->h.ctx->linePattern[rec->current_segment]) {
1362 				rec->segment_offset = 0;
1363 				if ( ++rec->current_segment >= rec->n_segments ) {
1364 					rec->current_segment = 0;
1365 					rec->segment_is_fg = true;
1366 				} else {
1367 					rec->segment_is_fg = !rec->segment_is_fg;
1368 				}
1369 			}
1370 
1371 			/* put pixel */
1372 			if (
1373 				(visibility > VISIBILITY_NONE) &&
1374 				(rec->h.color != NULL) &&
1375 				(
1376 					(visibility == VISIBILITY_CLEAR) ||
1377 					img_point_in_region(x1, y, rec->h.ctx->region)
1378 				)
1379 			)
1380 				setpixel(&rec->h, x1, y);
1381 		}
1382 	}
1383 }
1384 
1385 Bool
img_polyline(Handle dest,int n_points,Point * points,PImgPaintContext ctx)1386 img_polyline( Handle dest, int n_points, Point * points, PImgPaintContext ctx)
1387 {
1388 	PIcon i = (PIcon) dest;
1389 	int j;
1390 	int type     = i->type;
1391 	int maskType = kind_of(dest, CIcon) ? i->maskType : 0;
1392 	ImgSegmentedLineRec rec;
1393 	BoxRegionRec dummy_region;
1394 	Box dummy_region_box, *pbox;
1395 	Point* pp;
1396 	Rect  enclosure;
1397 	Bool closed;
1398 
1399 	if ( ctx->rop == ropNoOper || n_points <= 1) return true;
1400 
1401 	switch ( hline_init( &rec.h, dest, ctx, "img_polyline")) {
1402 	case HLINE_INIT_RETRY: {
1403 		Bool ok;
1404 		ok = img_polyline( dest, n_points, points, ctx);
1405 		if ( i-> options. optPreserveType ) {
1406 			if ( type != i->type )
1407 				CImage(dest)-> set_type( dest, type );
1408 			if ( maskType != 0 && maskType != i->maskType )
1409 				CIcon(dest)-> set_maskType( dest, maskType );
1410 		}
1411 		return ok;
1412 	}
1413 	case HLINE_INIT_FAIL:
1414 		return false;
1415 	}
1416 
1417 	rec.solid   = (strcmp((const char*)ctx->linePattern, (const char*)lpSolid) == 0);
1418 	if ( *(ctx->linePattern) == 0) {
1419 		if ( ctx->transparent ) return true;
1420 		rec.solid = true;
1421 		memcpy( ctx->color, ctx->backColor, MAX_SIZEOF_PIXEL);
1422 	}
1423 
1424 	if ( rec.solid )
1425 		rec.h.color = ctx->color;
1426 
1427 	/* patterns */
1428 	rec.n_segments       = strlen(( const char*) ctx->linePattern );
1429 	rec.current_segment  = 0;
1430 	rec.segment_offset   = 0;
1431 	rec.segment_is_fg    = 1;
1432 	if ( ctx->region == NULL ) {
1433 		dummy_region.n_boxes = 1;
1434 		dummy_region.boxes = &dummy_region_box;
1435 		dummy_region_box.x = 0;
1436 		dummy_region_box.y = 0;
1437 		dummy_region_box.width  = i->w;
1438 		dummy_region_box.height = i->h;
1439 		ctx->region = &dummy_region;
1440 	}
1441 	enclosure.left   = ctx->region->boxes[0].x;
1442 	enclosure.bottom = ctx->region->boxes[0].y;
1443 	enclosure.right  = ctx->region->boxes[0].x + ctx->region->boxes[0].width  - 1;
1444 	enclosure.top    = ctx->region->boxes[0].y + ctx->region->boxes[0].height - 1;
1445 	for ( j = 1, pbox = ctx->region->boxes + 1; j < ctx->region->n_boxes; j++, pbox++) {
1446 		int right = pbox->x + pbox->width - 1;
1447 		int top   = pbox->y + pbox->height - 1;
1448 		if ( enclosure.left   > pbox->x ) enclosure.left   = pbox->x;
1449 		if ( enclosure.bottom > pbox->y ) enclosure.bottom = pbox->y;
1450 		if ( enclosure.right  < right   ) enclosure.right  = right;
1451 		if ( enclosure.top    < top     ) enclosure.top    = top;
1452 	}
1453 
1454 	closed  = points[0].x == points[n_points-1].x && points[0].y == points[n_points-1].y && n_points > 2;
1455 	for ( j = 0, pp = points; j < n_points - 1; j++, pp++) {
1456 		/* calculate clipping: -1 invisible, 0 definitely clipped, 1 possibly clipped */
1457 		int visibility;
1458 		int curr_maj, curr_min, to_maj, delta_maj, delta_min;
1459 		int delta_y, delta_x;
1460 		int dir = 0, d, d_inc1, d_inc2;
1461 		int inc_maj, inc_min;
1462 		int x, y, acc_x = 0, acc_y = INT_MIN, ox;
1463 		Point a, b;
1464 
1465 		/* printf("* p(%d): (%d,%d)-(%d,%d)\n", j, pp[0].x, pp[0].y, pp[1].x, pp[1].y); */
1466 		a.x = pp[0].x + ctx->translate.x;
1467 		a.y = pp[0].y + ctx->translate.y;
1468 		b.x = pp[1].x + ctx->translate.x;
1469 		b.y = pp[1].y + ctx->translate.y;
1470 		if (a.x == b.x && a.y == b.y && n_points > 2) continue;
1471 
1472 		if (
1473 			( a.x < enclosure.left   && b.x < enclosure.left) ||
1474 			( a.x > enclosure.right  && b.x > enclosure.right) ||
1475 			( a.y < enclosure.bottom && b.y < enclosure.bottom) ||
1476 			( a.y > enclosure.top    && b.y > enclosure.top)
1477 		) {
1478 			visibility = VISIBILITY_NONE;
1479 			if ( rec.solid ) continue;
1480 		} else if (
1481 			a.x >= enclosure.left   && b.x >= enclosure.left &&
1482 			a.x <= enclosure.right  && b.x <= enclosure.right &&
1483 			a.y >= enclosure.bottom && b.y >= enclosure.bottom &&
1484 			a.y <= enclosure.top    && b.y <= enclosure.top
1485 		) {
1486 			if ( ctx->region->n_boxes > 1) {
1487 				int i,n;
1488 				Box *e;
1489 				visibility = VISIBILITY_CLIPPED;
1490 				for (
1491 					i = 0, e = ctx->region->boxes, n = ctx->region->n_boxes;
1492 					i < n; i++, e++
1493 				) {
1494 					int r = e->x + e->width;
1495 					int t = e->y + e->height;
1496 					if (
1497 						a.x >= e->x && a.y >= e->y && a.x < r && a.y < t &&
1498 						b.x >= e->x && b.y >= e->y && b.x < r && b.y < t
1499 					) {
1500 						visibility = VISIBILITY_CLEAR;
1501 						break;
1502 					}
1503 				}
1504 			} else {
1505 				visibility = VISIBILITY_CLEAR;
1506 			}
1507 		} else {
1508 			visibility = VISIBILITY_CLIPPED;
1509 		}
1510 
1511 /*
1512    Bresenham line plotting, (c) LiloHuang @ 2008, kenwu@cpan.org
1513    http://cpansearch.perl.org/src/KENWU/Algorithm-Line-Bresenham-C-0.1/Line/Bresenham/C/C.xs
1514  */
1515  		rec.skip_pixel = closed || (j > 0);
1516 		delta_y = b.y - a.y;
1517 		delta_x = b.x - a.x;
1518 		if (abs(delta_y) > abs(delta_x)) dir = 1;
1519 
1520 		if (dir) {
1521 			curr_maj = a.y;
1522 			curr_min = a.x;
1523 			to_maj = b.y;
1524 			delta_maj = delta_y;
1525 			delta_min = delta_x;
1526 		} else {
1527 			curr_maj = a.x;
1528 			curr_min = a.y;
1529 			to_maj = b.x;
1530 			delta_maj = delta_x;
1531 			delta_min = delta_y;
1532 		}
1533 
1534 		if (delta_maj != 0)
1535 			inc_maj = (abs(delta_maj)==delta_maj ? 1 : -1);
1536 		else
1537 			inc_maj = 0;
1538 
1539 		if (delta_min != 0)
1540 			inc_min = (abs(delta_min)==delta_min ? 1 : -1);
1541 		else
1542 			inc_min = 0;
1543 
1544 		delta_maj = abs(delta_maj);
1545 		delta_min = abs(delta_min);
1546 
1547 		d      = (delta_min << 1) - delta_maj;
1548 		d_inc1 = (delta_min << 1);
1549 		d_inc2 = ((delta_min - delta_maj) << 1);
1550 
1551 		x = INT_MIN;
1552 		while(1) {
1553 			ox = x;
1554 			if (dir) {
1555 				x = curr_min;
1556 				y = curr_maj;
1557 			} else {
1558 				x = curr_maj;
1559 				y = curr_min;
1560 			}
1561 			if ( acc_y != y ) {
1562 				if ( acc_y > INT_MIN)
1563 					segmented_hline( &rec, acc_x, ox, acc_y, visibility);
1564 				acc_x = x;
1565 				acc_y = y;
1566 			}
1567 
1568 			if (curr_maj == to_maj) break;
1569 			curr_maj += inc_maj;
1570 			if (d < 0) {
1571 				d += d_inc1;
1572 			} else {
1573 				d += d_inc2;
1574 				curr_min += inc_min;
1575 			}
1576 		}
1577 		if ( acc_y > INT_MIN)
1578 			segmented_hline( &rec, acc_x, x, acc_y, visibility);
1579 	}
1580 	return true;
1581 }
1582 
1583 typedef struct {
1584 	int dX;
1585 	int dY;
1586 	int bpp;
1587 	int sls;
1588 	int dls;
1589 	int mls;
1590 	int als;
1591 	Byte * src;
1592 	Byte * dst;
1593 	Byte * srcMask;
1594 	Byte * dstMask;
1595 	Bool use_src_alpha;
1596 	Bool use_dst_alpha;
1597 	Byte src_alpha_mul;
1598 	Byte dst_alpha_mul;
1599 	Byte * pmsbuf;
1600 	Byte * asbuf;
1601 	Byte * adbuf;
1602 	BlendFunc * blend1, * blend2;
1603 } ImgPutAlphaCallbackRec;
1604 
1605 static void
multiply(Byte * src,Byte * alpha,int alpha_step,Byte * dst,int bytes)1606 multiply( Byte * src, Byte * alpha, int alpha_step, Byte * dst, int bytes)
1607 {
1608 	while (bytes--) {
1609 		*(dst++) = *(src++) * *alpha / 255.0 + .5;
1610 		alpha += alpha_step;
1611 	}
1612 }
1613 
1614 static Bool
img_put_alpha_single(int x,int y,int w,int h,ImgPutAlphaCallbackRec * ptr)1615 img_put_alpha_single( int x, int y, int w, int h, ImgPutAlphaCallbackRec * ptr)
1616 {
1617 	int i;
1618 	const int bpp = ptr->bpp;
1619 	int bytes = w * bpp;
1620 	const int sls = ptr->sls;
1621 	const int dls = ptr->dls;
1622 	const int mls = ptr->mls;
1623 	const int als = ptr->als;
1624 	const Byte * s = ptr->src + (ptr->dY + y) * ptr->sls + (ptr->dX + x) * bpp;
1625 	const Byte * d = ptr->dst + y * ptr->dls + x * bpp;
1626 	const Byte * m = ( mls > 0) ? ptr->srcMask + (ptr->dY + y) * mls + (ptr->dX + x) : NULL;
1627 	const Byte * a = ( als > 0) ? ptr->dstMask + y * als + x : NULL;
1628 #ifdef HAVE_OPENMP
1629 #pragma omp parallel for
1630 #endif
1631 	for ( i = 0; i < h; i++) {
1632 		Byte *asbuf_ptr, *adbuf_ptr, *s_ptr, *m_ptr, *d_ptr, *a_ptr;
1633 
1634 		s_ptr = (Byte*)s + sls * i;
1635 		d_ptr = (Byte*)d + dls * i;
1636 		m_ptr = m ? (Byte*)m + mls * i : NULL;
1637 		a_ptr = a ? (Byte*)a + als * i : NULL;
1638 
1639 		if ( !ptr->use_src_alpha ) {
1640 			asbuf_ptr = ptr->asbuf + bytes * OMP_THREAD_NUM;
1641 			fill_alpha_buf( asbuf_ptr, m_ptr, w, bpp);
1642 			if ( ptr-> src_alpha_mul < 255 )
1643 				multiply( asbuf_ptr, &ptr->src_alpha_mul, 0, asbuf_ptr, bytes);
1644 		} else
1645 			asbuf_ptr = ptr->asbuf;
1646 
1647 		if ( !ptr->use_dst_alpha ) {
1648 			adbuf_ptr = ptr->adbuf + bytes * OMP_THREAD_NUM;
1649 			fill_alpha_buf( adbuf_ptr, a_ptr, w, bpp);
1650 			if ( ptr-> dst_alpha_mul < 255 )
1651 				multiply( adbuf_ptr, &ptr->dst_alpha_mul, 0, adbuf_ptr, bytes);
1652 		} else
1653 			adbuf_ptr = ptr->adbuf;
1654 
1655 		if ( ptr-> pmsbuf ) {
1656 			Byte *pmsbuf = ptr-> pmsbuf + bytes * OMP_THREAD_NUM;
1657 			multiply( s_ptr, asbuf_ptr, ptr->use_src_alpha ? 0 : 1, pmsbuf, bytes);
1658 			s_ptr = pmsbuf;
1659 		}
1660 		ptr->blend1(
1661 			s_ptr, 1,
1662 			asbuf_ptr, ptr->use_src_alpha ? 0 : 1,
1663 			d_ptr,
1664 			adbuf_ptr, ptr->use_dst_alpha ? 0 : 1,
1665 			bytes);
1666 		if (a == NULL)
1667 			continue;
1668 
1669 #define BLEND_ALPHA(src,step)\
1670 	ptr->blend2(\
1671 		src,step,\
1672 		src,step,\
1673 		(Byte*)a_ptr,\
1674 		a_ptr, ptr->use_dst_alpha ? 0 : 1,\
1675 		w)
1676 
1677 		if ( ptr->dst_alpha_mul < 255 )
1678 			multiply( a_ptr, &ptr->dst_alpha_mul, 0, a_ptr, w);
1679 
1680 		if ( ptr-> src_alpha_mul < 255 ) {
1681 			if ( bpp == 3 ) /* reuse the old values from multiply() otherwise */
1682 				multiply( m_ptr, &ptr->src_alpha_mul, 0, asbuf_ptr, w);
1683 			BLEND_ALPHA(asbuf_ptr,1);
1684 		} else if ( ptr->use_src_alpha )
1685 			BLEND_ALPHA(ptr->asbuf,0);
1686 		else
1687 			BLEND_ALPHA(m_ptr,1);
1688 #undef BLEND2
1689 	}
1690 	return true;
1691 }
1692 
1693 /*
1694 	This is basically a lightweight pixman_image_composite() .
1695 	Converts images to either 8 or 24 bits before processing
1696 */
1697 static Bool
img_put_alpha(Handle dest,Handle src,int dstX,int dstY,int srcX,int srcY,int dstW,int dstH,int srcW,int srcH,int rop,PBoxRegionRec region)1698 img_put_alpha( Handle dest, Handle src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH, int rop, PBoxRegionRec region)
1699 {
1700 	int bpp, bytes, mls, als, xrop;
1701 	unsigned int src_alpha = 0, dst_alpha = 0;
1702 	Bool use_src_alpha = false, use_dst_alpha = false, use_pms = false;
1703 	Byte *asbuf, *adbuf, *pmsbuf = NULL, src_alpha_mul = 255, dst_alpha_mul = 255;
1704 
1705 	xrop = rop;
1706 
1707 	/* differentiate between per-pixel alpha and a global value */
1708 	if ( rop & ropSrcAlpha ) {
1709 		use_src_alpha = true;
1710 		src_alpha = (rop >> ropSrcAlphaShift) & 0xff;
1711 	}
1712 	if ( rop & ropDstAlpha ) {
1713 		use_dst_alpha = true;
1714 		dst_alpha = (rop >> ropDstAlphaShift) & 0xff;
1715 	}
1716 	if ( rop & ropPremultiply )
1717 		use_pms = true;
1718 	rop &= ropPorterDuffMask;
1719 	if ( rop > ropMaxPDFunc || rop < 0 ) return false;
1720 
1721 	/* align types and geometry - can only operate over imByte and imRGB */
1722 	bpp = (( PImage(src)->type & imGrayScale) && ( PImage(dest)->type & imGrayScale)) ? imByte : imRGB;
1723 
1724 	/* adjust rectangles */
1725 	if ( dstX < 0 || dstY < 0 || dstX + dstW >= PImage(dest)-> w || dstY + dstH >= PImage(dest)-> h) {
1726 		if ( dstX < 0) {
1727 			dstW += dstX;
1728 			srcX -= dstX;
1729 			dstX = 0;
1730 		}
1731 		if ( dstY < 0) {
1732 			dstH += dstY;
1733 			srcY -= dstY;
1734 			dstY = 0;
1735 		}
1736 		if ( dstX + dstW > PImage(dest)-> w)
1737 			dstW = PImage(dest)-> w - dstX;
1738 		if ( dstY + dstH > PImage(dest)-> h)
1739 			dstH = PImage(dest)-> h - dstY;
1740 	}
1741 	if ( srcX + srcW > PImage(src)-> w)
1742 		srcW = PImage(src)-> w - srcX;
1743 	if ( srcY + srcH > PImage(src)-> h)
1744 		srcH = PImage(src)-> h - srcY;
1745 	if ( srcH <= 0 || srcW <= 0 )
1746 		return false;
1747 
1748 	/* adjust source type */
1749 	if (PImage(src)-> type != bpp || srcW != dstW || srcH != dstH ) {
1750 		Bool ok;
1751 		Handle dup;
1752 
1753 		if ( srcW != PImage(src)-> w || srcH != PImage(src)-> h)
1754 			dup = CImage(src)-> extract( src, srcX, srcY, srcW, srcH );
1755 		else
1756 			dup = CImage(src)-> dup(src);
1757 
1758 		if ( srcW != dstW || srcH != dstH )
1759 			CImage(dup)->stretch( dup, dstW, dstH );
1760 		if ( PImage( dup )-> type != bpp )
1761 			CImage(dup)-> set_type( dup, bpp);
1762 
1763 		ok = img_put_alpha( dest, dup, dstX, dstY, 0, 0, dstW, dstH, dstW, dstH, xrop, region);
1764 
1765 		Object_destroy(dup);
1766 		return ok;
1767 	}
1768 
1769 	/* adjust destination type */
1770 	if (PImage(dest)-> type != bpp || ( kind_of( dest, CIcon) && PIcon(dest)->maskType != imbpp8 )) {
1771 		Bool ok;
1772 		Bool icon = kind_of(dest, CIcon);
1773 		int type = PImage(dest)->type;
1774 		int mask = icon ? PIcon(dest)->maskType : 0;
1775 
1776 		if ( type != bpp )
1777 			CIcon(dest)-> set_type( dest, bpp );
1778 		if ( icon && mask != imbpp8 )
1779 			CIcon(dest)-> set_maskType( dest, imbpp8 );
1780 		ok = img_put_alpha( dest, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH, xrop, region);
1781 		if ( PImage(dest)-> options. optPreserveType ) {
1782 			if ( type != bpp )
1783 				CImage(dest)-> set_type( dest, type );
1784 			if ( icon && mask != imbpp8 )
1785 				CIcon(dest)-> set_maskType( dest, mask );
1786 		}
1787 		return ok;
1788 	}
1789 
1790 	/* assign pointers */
1791 	if ( srcW != dstW || srcH != dstH ||
1792 		PImage(src)->type != PImage(dest)->type || PImage(src)-> type != bpp)
1793 		croak("panic: assert failed for img_put_alpha: %s", "types and geometry");
1794 
1795 	bpp = ( bpp == imByte ) ? 1 : 3;
1796 	if ( kind_of(src, CIcon)) {
1797 		mls = PIcon(src)-> maskLine;
1798 		if ( PIcon(src)-> maskType != imbpp8)
1799 			croak("panic: assert failed for img_put_alpha: %s", "src mask type");
1800 		if ( use_src_alpha )
1801 			src_alpha_mul = src_alpha;
1802 		use_src_alpha = false;
1803 	} else {
1804 		mls = 0;
1805 	}
1806 
1807 	if ( kind_of(dest, CIcon)) {
1808 		als = PIcon(dest)-> maskLine;
1809 		if ( PIcon(dest)-> maskType != imbpp8)
1810 			croak("panic: assert failed for img_put_alpha: %s", "dst mask type");
1811 		if ( use_dst_alpha )
1812 			dst_alpha_mul = dst_alpha;
1813 		use_dst_alpha = false;
1814 	} else {
1815 		als = 0;
1816 	}
1817 
1818 	if ( !use_src_alpha && mls == 0) {
1819 		use_src_alpha = true;
1820 		src_alpha = 0xff;
1821 	}
1822 	if ( !use_dst_alpha && als == 0) {
1823 		use_dst_alpha = true;
1824 		dst_alpha = 0xff;
1825 	}
1826 	if ( use_pms && !use_src_alpha && mls == 0 )
1827 		use_pms = false;
1828 
1829 	/* make buffers */
1830 	bytes = dstW * bpp;
1831 	if ( !(asbuf = malloc(use_src_alpha ? 1 : (bytes * OMP_MAX_THREADS)))) {
1832 		warn("not enough memory");
1833 		return false;
1834 	}
1835 	if ( !(adbuf = malloc(use_dst_alpha ? 1 : (bytes * OMP_MAX_THREADS)))) {
1836 		free(asbuf);
1837 		warn("not enough memory");
1838 		return false;
1839 	}
1840 	if ( use_pms ) {
1841 		if ( !(pmsbuf = malloc( bytes * OMP_MAX_THREADS))) {
1842 			free(adbuf);
1843 			free(asbuf);
1844 			warn("not enough memory");
1845 			return false;
1846 		}
1847 	}
1848 
1849 	if ( use_src_alpha ) asbuf[0] = src_alpha;
1850 	if ( use_dst_alpha ) adbuf[0] = dst_alpha;
1851 
1852 	/* select function */
1853 	{
1854 		ImgPutAlphaCallbackRec rec = {
1855 			/* dX            */ srcX - dstX,
1856 			/* dY            */ srcY - dstY,
1857 			/* bpp           */ bpp,
1858 			/* sls           */ PImage(src )-> lineSize,
1859 			/* dls           */ PImage(dest)-> lineSize,
1860 			/* mls           */ mls,
1861 			/* als           */ als,
1862 			/* src           */ PImage(src )->data,
1863 			/* dst           */ PImage(dest)->data,
1864 			/* srcMask       */ (mls > 0) ? PIcon(src )->mask : NULL,
1865 			/* dstMask       */ (als > 0) ? PIcon(dest)->mask : NULL,
1866 			/* use_src_alpha */ use_src_alpha,
1867 			/* use_dst_alpha */ use_dst_alpha,
1868 			/* src_alpha_mul */ src_alpha_mul,
1869 			/* dst_alpha_mul */ dst_alpha_mul,
1870 			/* pmsbuf        */ pmsbuf,
1871 			/* asbuf         */ asbuf,
1872 			/* adbuf         */ adbuf,
1873 		};
1874 		find_blend_proc(rop, &rec.blend1, &rec.blend2);
1875 		img_region_foreach( region,
1876 			dstX, dstY, dstW, dstH,
1877 			(RegionCallbackFunc*)img_put_alpha_single, &rec
1878 		);
1879 	};
1880 
1881 	/* cleanup */
1882 	free(adbuf);
1883 	free(asbuf);
1884 	if (pmsbuf) free(pmsbuf);
1885 
1886 	return true;
1887 }
1888 
1889 typedef struct {
1890 	int bpp, als, dls, step, pat_x_offset;
1891 	Byte * dst, *dstMask, *pattern_buf, *adbuf;
1892 	Bool use_dst_alpha, solid;
1893 	Byte src_alpha;
1894 	PImgPaintContext ctx;
1895 	BlendFunc * blend1, * blend2;
1896 } ImgBarAlphaCallbackRec;
1897 
1898 static Bool
img_bar_alpha_single_opaque(int x,int y,int w,int h,ImgBarAlphaCallbackRec * ptr)1899 img_bar_alpha_single_opaque( int x, int y, int w, int h, ImgBarAlphaCallbackRec * ptr)
1900 {
1901 	int i;
1902 	const int bpp = ptr->bpp;
1903 	const int blt_bytes = w * bpp;
1904 	const int dls = ptr->dls;
1905 	const int als = ptr->als;
1906 	const Byte * d = ptr->dst + y * ptr->dls + x * bpp;
1907 	const Byte * a = (als > 0) ? ptr->dstMask + y * als + x : NULL;
1908 	int blt_step = (blt_bytes > ptr->step) ? ptr->step : blt_bytes;
1909 	Byte * pat_ptr;
1910 
1911 	if (!ptr->solid && (( ptr-> pat_x_offset % FILL_PATTERN_SIZE ) != (x % FILL_PATTERN_SIZE))) {
1912 		int dx = (x % FILL_PATTERN_SIZE) - ( ptr-> pat_x_offset % FILL_PATTERN_SIZE );
1913 		if ( dx < 0 ) dx += FILL_PATTERN_SIZE;
1914 		pat_ptr = ptr->pattern_buf + dx * bpp;
1915 		if ( blt_step + FILL_PATTERN_SIZE * bpp > BLT_BUFSIZE )
1916 			blt_step -= FILL_PATTERN_SIZE * bpp;
1917 	} else
1918 		pat_ptr = ptr->pattern_buf;
1919 
1920 	for ( i = 0; i < h; i++) {
1921 		Byte *adbuf_ptr;
1922 
1923 		int bytes = blt_bytes;
1924 		Byte *d_ptr = (Byte *)d;
1925 		Byte *s_ptr = pat_ptr + ((y + i) % FILL_PATTERN_SIZE) * ptr->step;
1926 
1927 		if ( !ptr->use_dst_alpha ) {
1928 			adbuf_ptr = ptr->adbuf;
1929 			fill_alpha_buf( adbuf_ptr, (Byte*)a, w, bpp);
1930 		} else
1931 			adbuf_ptr = ptr->adbuf;
1932 
1933 		while ( bytes > 0 ) {
1934 			ptr->blend1(
1935 				s_ptr, 1,
1936 				&ptr->src_alpha, 0,
1937 				d_ptr,
1938 				adbuf_ptr, ptr->use_dst_alpha ? 0 : 1,
1939 				( bytes > blt_step ) ? blt_step : bytes);
1940 			bytes -= blt_step;
1941 			d_ptr += blt_step;
1942 		}
1943 		d += dls;
1944 
1945 		if ( a ) {
1946 			ptr->blend2(
1947 				&ptr->src_alpha, 0,
1948 				&ptr->src_alpha, 0,
1949 				(Byte*)a,
1950 				a, ptr->use_dst_alpha ? 0 : 1,
1951 				w
1952 			);
1953 			a += als;
1954 		}
1955 	}
1956 	return true;
1957 }
1958 
1959 static Bool
img_bar_alpha_single_transparent(int x,int y,int w,int h,ImgBarAlphaCallbackRec * ptr)1960 img_bar_alpha_single_transparent( int x, int y, int w, int h, ImgBarAlphaCallbackRec * ptr)
1961 {
1962 	int i, j;
1963 	const int bpp = ptr->bpp;
1964 	const int blt_bytes = w * bpp;
1965 	const int dls = ptr->dls;
1966 	const int als = ptr->als;
1967 	const Byte * d = ptr->dst + y * ptr->dls + x * bpp;
1968 	const Byte * a = (als > 0) ? ptr->dstMask + y * als + x : NULL;
1969 
1970 	for ( i = 0; i < h; i++) {
1971 		unsigned int pat;
1972 		Byte *d_ptr, *a_ptr, *adbuf_ptr, *adbuf_ptr2;
1973 		pat = (unsigned int) ptr->ctx->pattern[(i + ptr->ctx->patternOffset. y) % FILL_PATTERN_SIZE];
1974 		if ( pat == 0 ) goto NEXT_LINE;
1975 		pat = (((pat << 8) | pat) >> ((ptr->ctx->patternOffset. x + 8 - (x % 8)) % FILL_PATTERN_SIZE)) & 0xff;
1976 
1977 		if ( !ptr->use_dst_alpha ) {
1978 			adbuf_ptr = ptr->adbuf;
1979 			fill_alpha_buf( adbuf_ptr, (Byte*)a, w, bpp);
1980 		} else
1981 			adbuf_ptr = ptr->adbuf;
1982 
1983 		if ( pat == 0xff && bpp == 1) {
1984 			ptr->blend1(
1985 				ptr->ctx->color, 0,
1986 				&ptr->src_alpha, 0,
1987 				(Byte*)d,
1988 				adbuf_ptr, ptr->use_dst_alpha ? 0 : 1,
1989 				blt_bytes);
1990 			if ( a ) ptr->blend2(
1991 				&ptr->src_alpha, 0,
1992 				&ptr->src_alpha, 0,
1993 				(Byte*)a,
1994 				a, ptr->use_dst_alpha ? 0 : 1,
1995 				w
1996 			);
1997 			goto NEXT_LINE;
1998 		}
1999 
2000 		for (
2001 			j = 0, d_ptr = (Byte*)d, a_ptr = (Byte*)a, adbuf_ptr2 = adbuf_ptr;
2002 			j < w;
2003 			j++
2004 		) {
2005 			if ( pat & (0x80 >> (j % 8)) ) {
2006 				ptr->blend1(
2007 					ptr->ctx->color, 0,
2008 					&ptr->src_alpha, 0,
2009 					d_ptr,
2010 					adbuf_ptr2, ptr->use_dst_alpha ? 0 : 1,
2011 					bpp);
2012 				if ( a ) ptr->blend2(
2013 					&ptr->src_alpha, 0,
2014 					&ptr->src_alpha, 0,
2015 					a_ptr,
2016 					a_ptr, ptr->use_dst_alpha ? 0 : 1,
2017 					1
2018 				);
2019 			}
2020 			d_ptr += bpp;
2021 			if ( a ) a_ptr++;
2022 			if ( !ptr-> use_dst_alpha ) adbuf_ptr2++;
2023 		}
2024 
2025 	NEXT_LINE:
2026 		d += dls;
2027 		if ( a ) a += als;
2028 	}
2029 	return true;
2030 }
2031 
2032 static Bool
img_bar_alpha(Handle dest,int x,int y,int w,int h,PImgPaintContext ctx)2033 img_bar_alpha( Handle dest, int x, int y, int w, int h, PImgPaintContext ctx)
2034 {
2035 	int bpp, als;
2036 	unsigned int src_alpha = 0xff, dst_alpha = 0;
2037 	Bool use_dst_alpha = false, solid;
2038 	Byte blt_buffer[BLT_BUFSIZE], *adbuf;
2039 	int j, k, blt_bytes, blt_step = -1;
2040 
2041 	if ( ctx->transparent && (memcmp( ctx->pattern, fillPatterns[fpEmpty], sizeof(FillPattern)) == 0))
2042 		return true;
2043 
2044 	/* align types and geometry - can only operate over imByte and imRGB */
2045 	bpp = ( PImage(dest)->type & imGrayScale) ? imByte : imRGB;
2046 	if (PImage(dest)-> type != bpp || ( kind_of( dest, CIcon) && PIcon(dest)->maskType != imbpp8 )) {
2047 		Bool icon = kind_of(dest, CIcon), ok;
2048 		int type = PImage(dest)->type;
2049 		int mask = icon ? PIcon(dest)->maskType : 0;
2050 
2051 		if ( type != bpp ) {
2052 			resample_colors( dest, bpp, ctx );
2053 			CIcon(dest)-> set_type( dest, bpp );
2054 		}
2055 		if ( icon && mask != imbpp8 )
2056 			CIcon(dest)-> set_maskType( dest, imbpp8 );
2057 		ok = img_bar_alpha( dest, x, y, w, h, ctx);
2058 		if ( PImage(dest)-> options. optPreserveType ) {
2059 			if ( type != bpp )
2060 				CImage(dest)-> set_type( dest, type );
2061 			if ( icon && mask != imbpp8 )
2062 				CIcon(dest)-> set_maskType( dest, mask );
2063 		}
2064 		return ok;
2065 	}
2066 
2067 	/* differentiate between per-pixel alpha and a global value */
2068 	if ( ctx->rop & ropSrcAlpha )
2069 		src_alpha = (ctx->rop >> ropSrcAlphaShift) & 0xff;
2070 	if ( ctx->rop & ropDstAlpha ) {
2071 		use_dst_alpha = true;
2072 		dst_alpha = (ctx->rop >> ropDstAlphaShift) & 0xff;
2073 	}
2074 	ctx->rop &= ropPorterDuffMask;
2075 	if ( ctx->rop > ropMaxPDFunc || ctx->rop < 0 ) ctx->rop = ropSrcOver;
2076 
2077 	/* assign pointers */
2078 	bpp = ( bpp == imByte ) ? 1 : 3;
2079 	if ( kind_of(dest, CIcon)) {
2080 		als = PIcon(dest)-> maskLine;
2081 		if ( PIcon(dest)-> maskType != imbpp8)
2082 			croak("panic: assert failed for img_bar_alpha: %s", "dst mask type");
2083 		use_dst_alpha = false;
2084 	} else {
2085 		als = 0;
2086 	}
2087 
2088 	if ( !use_dst_alpha && als == 0) {
2089 		use_dst_alpha = true;
2090 		dst_alpha = 0xff;
2091 	}
2092 	if ( !(adbuf = malloc(use_dst_alpha ? 1 : (bpp * w)))) {
2093 		warn("not enough memory");
2094 		return false;
2095 	}
2096 	if ( use_dst_alpha ) adbuf[0] = dst_alpha;
2097 
2098 	/* premultiply colors */
2099 	for ( j = 0; j < bpp; j++) {
2100 		ctx->color[j] = (float)(ctx->color[j] * src_alpha) / 255.0 + .5;
2101 		ctx->backColor[j] = (float)(ctx->backColor[j] * src_alpha) / 255.0 + .5;
2102 	}
2103 
2104 	solid = (memcmp( ctx->pattern, fillPatterns[fpSolid], sizeof(FillPattern)) == 0);
2105 	if ( solid || !ctx->transparent ) {
2106 		/* render a (minimum) 8x8xPIXEL matrix with pattern, then
2107 		replicate it over blt_buffer as much as possible, to streamline
2108 		byte operations */
2109 		blt_bytes = w * bpp;
2110 		if ( blt_bytes < FILL_PATTERN_SIZE * bpp ) blt_bytes = FILL_PATTERN_SIZE * bpp;
2111 		blt_bytes *= FILL_PATTERN_SIZE;
2112 		blt_step = ((blt_bytes > BLT_BUFSIZE) ? BLT_BUFSIZE : blt_bytes) / FILL_PATTERN_SIZE;
2113 		if ( bpp > 1 )
2114 			blt_step = (blt_step / bpp / FILL_PATTERN_SIZE) * bpp * FILL_PATTERN_SIZE;
2115 		for ( j = 0; j < FILL_PATTERN_SIZE; j++) {
2116 			unsigned int pat, strip_size;
2117 			Byte matrix[MAX_SIZEOF_PIXEL * FILL_PATTERN_SIZE], *buffer;
2118 			if ( solid ) {
2119 				pat = 0xff;
2120 			} else {
2121 				pat = (unsigned int) ctx->pattern[(j + ctx->patternOffset. y) % FILL_PATTERN_SIZE];
2122 				pat = (((pat << 8) | pat) >> ((ctx->patternOffset. x + 8 - (x % 8)) % FILL_PATTERN_SIZE)) & 0xff;
2123 			}
2124 			buffer = blt_buffer + j * blt_step;
2125 			if ( bpp == 1 ) {
2126 				strip_size = FILL_PATTERN_SIZE;
2127 				for ( k = 0; k < FILL_PATTERN_SIZE; k++)
2128 					matrix[k] = *((pat & (0x80 >> k)) ? ctx->color : ctx->backColor);
2129 			} else {
2130 				strip_size = FILL_PATTERN_SIZE * bpp;
2131 				for ( k = 0; k < FILL_PATTERN_SIZE; k++) {
2132 					Byte * color = (pat & (0x80 >> k)) ? ctx->color : ctx->backColor;
2133 					memcpy( matrix + k * bpp, color, bpp);
2134 				}
2135 			}
2136 			if ( strip_size > 1 ) {
2137 				Byte * buf = buffer;
2138 				for ( k = 0; k < blt_step / strip_size; k++, buf += strip_size)
2139 					memcpy( buf, matrix, strip_size);
2140 				if ( blt_step % strip_size != 0)
2141 					memcpy( buf, matrix, blt_step % strip_size);
2142 			}
2143 		}
2144 
2145 		/*
2146 		printf("bpp:%d step:%d\n", bpp, blt_step);
2147 		for ( j = 0; j < 8; j++) {
2148 			printf("%d: ", j);
2149 			for ( k = 0; k < blt_step; k++) {
2150 				printf("%02x", blt_buffer[ j * blt_step + k]);
2151 			}
2152 			printf("\n");
2153 		}
2154 		*/
2155 	}
2156 
2157 	/* select function */
2158 	{
2159 		ImgBarAlphaCallbackRec rec = {
2160 			/* bpp           */ bpp,
2161 			/* als           */ als,
2162 			/* dls           */ PImage(dest)-> lineSize,
2163 			/* step          */ blt_step,
2164 			/* pat_x_offset  */ x,
2165 			/* dst           */ PImage(dest)->data,
2166 			/* dstMask       */ (als > 0) ? PIcon(dest)->mask : NULL,
2167 			/* pattern_buf   */ blt_buffer,
2168 			/* adbuf         */ adbuf,
2169 			/* use_dst_alpha */ use_dst_alpha,
2170 			/* solid         */ solid,
2171 			/* src_alpha     */ src_alpha,
2172 			/* ctx           */ ctx
2173 		};
2174 		find_blend_proc(ctx->rop, &rec.blend1, &rec.blend2);
2175 		img_region_foreach( ctx->region, x, y, w, h,
2176 			( RegionCallbackFunc *)((solid || !ctx->transparent) ?
2177 				img_bar_alpha_single_opaque : img_bar_alpha_single_transparent),
2178 			&rec
2179 		);
2180 	};
2181 
2182 	free(adbuf);
2183 
2184 	return true;
2185 }
2186 
2187 typedef struct {
2188 	PIcon         i;
2189 	Rect          clip;
2190 	int           y, bpp, bytes;
2191 	Byte   *      color;
2192 	Bool          single_border;
2193 	int           first;
2194 	PList  *      lists;
2195 	PBoxRegionRec new_region;
2196 	int           new_region_size;
2197 } FillSession;
2198 
2199 static Bool
fs_get_pixel(FillSession * fs,int x,int y)2200 fs_get_pixel( FillSession * fs, int x, int y)
2201 {
2202 	Byte * data;
2203 
2204 
2205 	if ( x < fs-> clip. left || x > fs-> clip. right || y < fs-> clip. bottom || y > fs-> clip. top)
2206 		return false;
2207 
2208 	if ( fs-> lists[ y - fs-> first]) {
2209 		PList l = fs-> lists[ y - fs-> first];
2210 		int i;
2211 		for ( i = 0; i < l-> count; i+=2) {
2212 			if (((int) l-> items[i+1] >= x) && ((int)l->items[i] <= x))
2213 				return false;
2214 		}
2215 	}
2216 
2217 	data = fs->i->data + fs->i->lineSize * y;
2218 
2219 	switch( fs-> bpp) {
2220 	case 1: {
2221 		Byte xz = *(data + (x >> 3));
2222 		Byte v  = ( xz & ( 0x80 >> ( x & 7)) ? 1 : 0);
2223 		return fs-> single_border ?
2224 			( v == *(fs-> color)) : ( v != *(fs-> color));
2225 	}
2226 	case 4: {
2227 		Byte xz = *(data + (x >> 1));
2228 		Byte v  = (x & 1) ? ( xz & 0xF) : ( xz >> 4);
2229 		return fs-> single_border ?
2230 			( v == *(fs-> color)) : ( v != *(fs-> color));
2231 	}
2232 	case 8:
2233 		return fs-> single_border ?
2234 			( *(fs-> color) == *(data + x) ):
2235 			( *(fs-> color) != *(data + x) );
2236 	case 16:
2237 		return fs-> single_border ?
2238 			( *((uint16_t*)(fs-> color)) == *((uint16_t*)data + x) ) :
2239 			( *((uint16_t*)(fs-> color)) != *((uint16_t*)data + x) );
2240 	case 32:
2241 		return fs-> single_border ?
2242 			( *((uint32_t*)(fs-> color)) == *((uint32_t*)data + x) ) :
2243 			( *((uint32_t*)(fs-> color)) != *((uint32_t*)data + x) );
2244 	default: {
2245 		return fs-> single_border ?
2246 			( memcmp(data + x * fs->bytes, fs->color, fs->bytes) == 0) :
2247 			( memcmp(data + x * fs->bytes, fs->color, fs->bytes) != 0);
2248 	}}
2249 }
2250 
2251 static void
fs_hline(FillSession * fs,int x1,int y,int x2)2252 fs_hline( FillSession * fs, int x1, int y, int x2)
2253 {
2254 	y -= fs-> first;
2255 	if ( fs-> lists[y] == NULL)
2256 		fs-> lists[y] = plist_create( 32, 128);
2257 	list_add( fs-> lists[y], ( Handle) x1);
2258 	list_add( fs-> lists[y], ( Handle) x2);
2259 }
2260 
2261 static int
fs_fill(FillSession * fs,int sx,int sy,int d,int pxl,int pxr)2262 fs_fill( FillSession * fs, int sx, int sy, int d, int pxl, int pxr)
2263 {
2264 	int x, xr = sx;
2265 	while ( sx > fs-> clip. left  && fs_get_pixel( fs, sx - 1, sy)) sx--;
2266 	while ( xr < fs-> clip. right && fs_get_pixel( fs, xr + 1, sy)) xr++;
2267 	fs_hline( fs, sx, sy, xr);
2268 
2269 	if ( sy + d >= fs-> clip. bottom && sy + d <= fs-> clip. top) {
2270 		x = sx;
2271 		while ( x <= xr) {
2272 			if ( fs_get_pixel( fs, x, sy + d))
2273 				x = fs_fill( fs, x, sy + d, d, sx, xr);
2274 			x++;
2275 		}
2276 	}
2277 
2278 	if ( sy - d >= fs-> clip. bottom && sy - d <= fs-> clip. top) {
2279 		x = sx;
2280 		while ( x < pxl) {
2281 			if ( fs_get_pixel( fs, x, sy - d))
2282 				x = fs_fill( fs, x, sy - d, -d, sx, xr);
2283 			x++;
2284 		}
2285 		x = pxr;
2286 		while ( x <= xr) {
2287 			if ( fs_get_pixel( fs, x, sy - d))
2288 				x = fs_fill( fs, x, sy - d, -d, sx, xr);
2289 			x++;
2290 		}
2291 	}
2292 	return xr;
2293 }
2294 
2295 static Bool
fs_intersect(int x1,int y,int w,int h,FillSession * fs)2296 fs_intersect( int x1, int y, int w, int h, FillSession * fs)
2297 {
2298 	PList l;
2299 	Handle * items;
2300 	int i, j, x2;
2301 
2302 	x2 = x1 + w - 1;
2303 	for ( i = 0; i < h; i++)
2304 		if (( l = fs-> lists[y + i - fs->first]) != NULL )
2305 			for ( j = 0, items = l->items; j < l-> count; j+=2) {
2306 				Box * box;
2307 				int left  = (int) (*(items++));
2308 				int right = (int) (*(items++));
2309 				if ( left < x1 )
2310 					left = x1;
2311 				if ( right > x2)
2312 					right = x2;
2313 				if ( left > right )
2314 					continue;
2315 				if ( fs-> new_region-> n_boxes >= fs-> new_region_size ) {
2316 					PBoxRegionRec n;
2317 					fs-> new_region_size *= 2;
2318 					if ( !( n = img_region_alloc( fs-> new_region, fs-> new_region_size)))
2319 						return false;
2320 					fs-> new_region = n;
2321 				}
2322 				box = fs-> new_region-> boxes + fs-> new_region-> n_boxes;
2323 				box-> x      = left;
2324 				box-> y      = y + i;
2325 				box-> width  = right - left + 1;
2326 				box-> height = 1;
2327 				fs-> new_region-> n_boxes++;
2328 			}
2329 
2330 	return true;
2331 }
2332 
2333 Bool
img_flood_fill(Handle self,int x,int y,ColorPixel color,Bool single_border,PImgPaintContext ctx)2334 img_flood_fill( Handle self, int x, int y, ColorPixel color, Bool single_border, PImgPaintContext ctx)
2335 {
2336 	Bool ok = true;
2337 	Box box;
2338 	FillSession fs;
2339 
2340 	fs.i             = ( PIcon ) self;
2341 	fs.color         = color;
2342 	fs.bpp           = fs.i->type & imBPP;
2343 	fs.bytes         = fs.bpp / 8;
2344 	fs.single_border = single_border;
2345 
2346 	if ( ctx-> region ) {
2347 		Box box = img_region_box( ctx-> region );
2348 		fs. clip. left   = box. x;
2349 		fs. clip. bottom = box. y;
2350 		fs. clip. right  = box. x + box. width - 1;
2351 		fs. clip. top    = box. y + box. height - 1;
2352 	} else {
2353 		fs. clip. left   = 0;
2354 		fs. clip. bottom = 0;
2355 		fs. clip. right  = fs.i->w - 1;
2356 		fs. clip. top    = fs.i->h - 1;
2357 	}
2358 
2359 	fs. new_region_size = (fs. clip. top - fs. clip. bottom + 1) * 4;
2360 	if ( !( fs. new_region = img_region_alloc(NULL, fs. new_region_size)))
2361 		return false;
2362 
2363 	fs. first = fs. clip. bottom;
2364 	if ( !( fs. lists = malloc(( fs. clip. top - fs. clip. bottom + 1) * sizeof( void*)))) {
2365 		free( fs. new_region );
2366 		return false;
2367 	}
2368 	bzero( fs. lists, ( fs. clip. top - fs. clip. bottom + 1) * sizeof( void*));
2369 
2370 	if ( fs_get_pixel( &fs, x, y)) {
2371 		fs_fill( &fs, x, y, -1, x, x);
2372 		ok = img_region_foreach( ctx->region,
2373 			0, 0, fs.i->w, fs.i->h,
2374 			(RegionCallbackFunc*)fs_intersect, &fs
2375 		);
2376 	}
2377 
2378 	for ( x = 0; x < fs. clip. bottom - fs. clip. top + 1; x++)
2379 		if ( fs. lists[x])
2380 			plist_destroy( fs.lists[x]);
2381 	free( fs. lists);
2382 
2383 	if ( ok ) {
2384 		ctx-> region = fs. new_region;
2385 		box = img_region_box( ctx-> region );
2386 		ok = img_bar( self, box.x, box.y, box.width, box.height, ctx);
2387 	}
2388 
2389 	free( fs. new_region);
2390 
2391 	return ok;
2392 }
2393 
2394 /* alpha stuff */
2395 void
img_premultiply_alpha_constant(Handle self,int alpha)2396 img_premultiply_alpha_constant( Handle self, int alpha)
2397 {
2398 	Byte * data;
2399 	int i, j, pixels;
2400 	if ( PImage(self)-> type == imByte ) {
2401 		pixels = 1;
2402 	} else if ( PImage(self)-> type == imRGB ) {
2403 		pixels = 3;
2404 	} else {
2405 		croak("Not implemented");
2406 	}
2407 
2408 	data = PImage(self)-> data;
2409 	for ( i = 0; i < PImage(self)-> h; i++) {
2410 		register Byte *d = data, k;
2411 		for ( j = 0; j < PImage(self)-> w; j++ ) {
2412 			for ( k = 0; k < pixels; k++, d++)
2413 				*d = (alpha * *d) / 255.0 + .5;
2414 		}
2415 		data += PImage(self)-> lineSize;
2416 	}
2417 }
2418 
img_premultiply_alpha_map(Handle self,Handle alpha)2419 void img_premultiply_alpha_map( Handle self, Handle alpha)
2420 {
2421 	Byte * data, * mask;
2422 	int i, pixels;
2423 	if ( PImage(self)-> type == imByte ) {
2424 		pixels = 1;
2425 	} else if ( PImage(self)-> type == imRGB ) {
2426 		pixels = 3;
2427 	} else {
2428 		croak("Not implemented");
2429 	}
2430 
2431 	if ( PImage(alpha)-> type != imByte )
2432 		croak("Not implemented");
2433 
2434 	data = PImage(self)-> data;
2435 	mask = PImage(alpha)-> data;
2436 	for ( i = 0; i < PImage(self)-> h; i++) {
2437 		int j;
2438 		register Byte *d = data, *m = mask, k;
2439 		for ( j = 0; j < PImage(self)-> w; j++ ) {
2440 			register uint16_t alpha = *m++;
2441 			for ( k = 0; k < pixels; k++, d++)
2442 				*d = (alpha * *d) / 255.0 + .5;
2443 		}
2444 		data += PImage(self)-> lineSize;
2445 		mask += PImage(alpha)-> lineSize;
2446 	}
2447 }
2448 
2449 #ifdef __cplusplus
2450 }
2451 #endif
2452 
2453