1/***********************************************************************
2 *                                                                     *
3 * $Id: hpgspaintimage.c 281 2006-02-25 19:40:31Z softadm $
4 *                                                                     *
5 * hpgs - HPGl Script, a hpgl/2 interpreter, which uses a Postscript   *
6 *        API for rendering a scene and thus renders to a variety of   *
7 *        devices and fileformats.                                     *
8 *                                                                     *
9 * (C) 2004-2006 ev-i Informationstechnologie GmbH  http://www.ev-i.at *
10 *                                                                     *
11 * Author: Wolfgang Glas                                               *
12 *                                                                     *
13 *  hpgs is free software; you can redistribute it and/or              *
14 * modify it under the terms of the GNU Lesser General Public          *
15 * License as published by the Free Software Foundation; either        *
16 * version 2.1 of the License, or (at your option) any later version.  *
17 *                                                                     *
18 * hpgs is distributed in the hope that it will be useful,             *
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of      *
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   *
21 * Lesser General Public License for more details.                     *
22 *                                                                     *
23 * You should have received a copy of the GNU Lesser General Public    *
24 * License along with this library; if not, write to the               *
25 * Free Software  Foundation, Inc., 59 Temple Place, Suite 330,        *
26 * Boston, MA  02111-1307  USA                                         *
27 *                                                                     *
28 ***********************************************************************
29 *                                                                     *
30 * The implementation of the public API for the pixel painter.         *
31 *                                                                     *
32 ***********************************************************************/
33
34#include<hpgspaint.h>
35#include<math.h>
36#include<assert.h>
37#include<string.h>
38
39//#define HPGS_PAINT_IMAGE_DEBUG
40
41#ifdef HPGS_PAINT_IMAGE_DEBUG
42static void log_mat_6(const char *lead, const double *mat)
43{
44  hpgs_log("%s%10lg,%10lg,%10lg\n%*s%10lg,%10lg,%10lg.\n",
45           lead,mat->dx,mat->mxx,mat->mxy,
46           strlen(lead)," ",mat->dy,mat->myx,mat->myy);
47
48}
49#endif
50
51// multiplicates the given vector with the specified matrix.
52// This is here in order to allow efficient inlining of the code.
53//
54static void mat_6_mul(hpgs_point *pout, const hpgs_matrix *mat, double xin, double yin)
55{
56  pout->x = mat->dx + mat->mxx * xin + mat->mxy * yin;
57  pout->y = mat->dy + mat->myx * xin + mat->myy * yin;
58}
59
60// build matrix from destination to source pixel coordinates.
61static void build_inv_matrix(hpgs_matrix *m,
62                             const hpgs_paint_clipper *c,
63                             const hpgs_image *dest,
64                             const hpgs_image *src,
65                             const hpgs_point *ll,
66                             const hpgs_point *ul,
67                             const hpgs_point *lr,
68                             const hpgs_point *ur)
69{
70  // image to device coordinates.
71  hpgs_matrix a = { ul->x, ul->y,
72                    (lr->x-ll->x)/src->width, (lr->x-ur->x)/src->height,
73                    (lr->y-ll->y)/src->width, (lr->y-ur->y)/src->height };
74
75  // device to target raster coordinates.
76  hpgs_matrix b = {-0.5-c->bb.llx*dest->width/(c->bb.urx-c->bb.llx), c->y0/c->yfac,
77                   dest->width/(c->bb.urx-c->bb.llx), 0.0,
78                   0.0,                              -1.0/c->yfac };
79
80  hpgs_matrix tmp;
81
82  // take half a pixel into account.
83  a.dx += 0.5 * (a.mxx + a.mxy);
84  a.dy += 0.5 * (a.myx + a.myy);
85
86  hpgs_matrix_concat(&tmp,&b,&a);
87#ifdef HPGS_PAINT_IMAGE_DEBUG
88  log_mat_6("a = ",a);
89  log_mat_6("b = ",b);
90  log_mat_6("b*a = ",tmp);
91#endif
92  hpgs_matrix_invert(m,&tmp);
93}
94
95typedef int (*put_point_func_t)(hpgs_image *, const hpgs_image *,
96                                int, int, const hpgs_point *, double);
97
98static int put_point_0(hpgs_image *dest, const hpgs_image *src,
99                       int dest_i, int dest_j, const hpgs_point *src_point,
100                       double alpha)
101{
102  hpgs_paint_color col;
103
104  double a;
105
106  int src_i = (int)rint(src_point->x);
107  int src_j = (int)rint(src_point->y);
108
109  if (src_i < 0 || src_i >= src->width) return 0;
110  if (src_j < 0 || src_j >= src->height) return 0;
111
112  hpgs_image_get_pixel(src,src_i,src_j,&col,&a);
113
114  if (hpgs_image_define_color (dest,&col))
115    return -1;
116
117  return hpgs_image_rop3_pixel(dest,dest_i,dest_j,&col,
118                               alpha*a);
119}
120
121static int put_point_1(hpgs_image *dest, const hpgs_image *src,
122                       int dest_i, int dest_j, const hpgs_point *src_point,
123                       double alpha)
124{
125  hpgs_paint_color col;
126
127  double aa,a=0.0;
128  double r=0.0,g=0.0,b=0.0;
129
130  double w;
131  double sw = 0.0;
132
133  int src_i = (int)ceil(src_point->x);
134  int src_j = (int)ceil(src_point->y);
135
136  double wx = ceil(src_point->x) - src_point->x;
137  double wy = ceil(src_point->y) - src_point->y;
138
139  if (src_i < 0 || src_i > src->width) return 0;
140  if (src_j < 0 || src_j > src->height) return 0;
141
142  if (src_i > 0)
143    {
144      if (src_j > 0)
145        {
146          hpgs_image_get_pixel(src,src_i-1,src_j-1,&col,&aa);
147          w = wx+wy;
148          r+=w*col.r;
149          g+=w*col.g;
150          b+=w*col.b;
151          a += w*aa;
152          sw += w;
153        }
154
155      if (src_j < src->height)
156        {
157          hpgs_image_get_pixel(src,src_i-1,src_j,&col,&aa);
158          w = 1.0-wy+wx;
159          r+=w*col.r;
160          g+=w*col.g;
161          b+=w*col.b;
162          a += w*aa;
163          sw += w;
164        }
165    }
166
167  if (src_i < src->width)
168    {
169      if (src_j > 0)
170        {
171          hpgs_image_get_pixel(src,src_i,src_j-1,&col,&aa);
172          w = 1.0-wx+wy;
173          r+=w*col.r;
174          g+=w*col.g;
175          b+=w*col.b;
176          a += w*aa;
177          sw += w;
178        }
179
180      if (src_j < src->height)
181        {
182          hpgs_image_get_pixel(src,src_i,src_j,&col,&aa);
183          w=2.0-wx-wy;
184          r+=w*col.r;
185          g+=w*col.g;
186          b+=w*col.b;
187          a += w*aa;
188          sw += w;
189        }
190    }
191
192  if (!sw) return 0;
193
194  col.r = r / sw;
195  col.g = g / sw;
196  col.b = b / sw;
197
198  if (hpgs_image_define_color (dest,&col))
199    return -1;
200
201  return hpgs_image_rop3_pixel(dest,dest_i,dest_j,&col,
202                               0.25*alpha*a);
203}
204
205/*!
206
207    Draws the intersection of the given image with the current clip
208    path of the paint device \c pdv to the destination image of \c pdv.
209
210    The arguments \c ll, \c lr and \c ur are the
211    lower left, lower right and upper right corner of the drawn image
212    in world coordinates.
213
214    Return values:
215
216    \li 0 Sucess.
217    \li -1 An error ocurred.
218            Call \c hpgs_device_get_error in order to retrieve the error message.
219
220*/
221int hpgs_paint_device_drawimage(hpgs_paint_device *pdv,
222                                const hpgs_image *img,
223                                const hpgs_point *ll, const hpgs_point *lr,
224                                const hpgs_point *ur)
225{
226  const hpgs_paint_clipper *c = pdv->clippers[pdv->current_clipper];
227  put_point_func_t put_point_func = put_point_0;
228  hpgs_point ul;
229  double ll_i,lr_i,ur_i,ul_i;
230  int iscan0;
231  int iscan1;
232  int iscan;
233  hpgs_matrix imat[6];
234
235  if (pdv->image_interpolation)
236    put_point_func = put_point_1;
237
238  ul.x = ll->x + (ur->x - lr->x);
239  ul.y = ll->y + (ur->y - lr->y);
240
241  build_inv_matrix(imat,c,pdv->image,img,ll,&ul,lr,ur);
242
243  ll_i = (c->y0 - ll->y)/c->yfac;
244  lr_i = (c->y0 - lr->y)/c->yfac;
245  ur_i = (c->y0 - ur->y)/c->yfac;
246  ul_i = (c->y0 - ul.y)/c->yfac;
247
248  iscan0 = (int)ceil(ll_i);
249  if (lr_i < iscan0) iscan0 = (int)ceil(lr_i);
250  if (ur_i < iscan0) iscan0 = (int)ceil(ur_i);
251  if (ul_i < iscan0) iscan0 = (int)ceil(ul_i);
252
253  if (iscan0 < c->iscan0) iscan0 = c->iscan0;
254
255  iscan1 = (int)floor(ll_i);
256  if (lr_i > iscan1) iscan1 = (int)floor(lr_i);
257  if (ur_i > iscan1) iscan1 = (int)floor(ur_i);
258  if (ul_i > iscan1) iscan1 = (int)floor(ul_i);
259
260  if (iscan1 > c->iscan1) iscan1 = c->iscan1;
261
262#ifdef HPGS_PAINT_IMAGE_DEBUG
263  hpgs_log("ll_i,lr_i,ur_i,ul_i,iscan0,iscan1=%lg,%lg,%lg,%lg,%d,%d.\n",
264           ll_i,lr_i,ur_i,ul_i,iscan0,iscan1);
265
266  print_mat_6(stderr,"imat = ",imat);
267#endif
268
269  for (iscan = iscan0; iscan <= iscan1; ++iscan)
270    {
271      double x[2];
272      int ix = 0;
273      int io;
274      const hpgs_paint_scanline *scanline = c->scanlines+iscan;
275
276      if ((iscan > ll_i && iscan < lr_i) ||
277          (iscan < ll_i && iscan > lr_i)   )
278        {
279          x[ix] = ((ll_i - iscan)*lr->x + (iscan - lr_i)*ll->x) / (ll_i-lr_i);
280          ++ix;
281        }
282
283      if ((iscan > lr_i && iscan < ur_i) ||
284          (iscan < lr_i && iscan > ur_i)   )
285        {
286          x[ix] = ((lr_i - iscan)*ur->x + (iscan - ur_i)*lr->x) / (lr_i-ur_i);
287          ++ix;
288        }
289      if ((iscan > ur_i && iscan < ul_i) ||
290          (iscan < ur_i && iscan > ul_i)   )
291        {
292          x[ix] = ((ur_i - iscan)*ul.x + (iscan - ul_i)*ur->x) / (ur_i-ul_i);
293          ++ix;
294        }
295      if ((iscan > ul_i && iscan < ll_i) ||
296          (iscan < ul_i && iscan > ll_i)   )
297        {
298          x[ix] = ((ul_i - iscan)*ll->x + (iscan - ll_i)*ul.x) / (ul_i-ll_i);
299          ++ix;
300        }
301
302      if (ix!=2) continue;
303
304      if (x[0] > x[1])
305        {
306          double tmp = x[0];
307          x[0] = x[1];
308          x[1] = tmp;
309        }
310
311      if (pdv->overscan)
312        {
313          int out_state = 0;
314          io = 0;
315
316          // go through clip segments antialiased
317          while (io<scanline->n_points)
318            {
319              double xleft,xright,ixleft,ixright;
320              double ileft,iright,ii,alpha_left,alpha_right;
321              hpgs_point src_point;
322
323              xleft= scanline->points[io].x;
324
325              while (io<scanline->n_points && scanline->points[io].x == xleft)
326                {
327                  out_state += scanline->points[io].order;
328                  ++io;
329                }
330
331              if (io >= scanline->n_points) break;
332
333              alpha_left = out_state/256.0;
334
335              xright = scanline->points[io].x;
336              alpha_right = (out_state+scanline->points[io].order)/256.0;
337
338              ixright = xright;
339              ixleft = xleft;
340
341              if (ixleft < x[0]) ixleft = x[0];
342              if (ixleft > x[1]) ixleft = x[1];
343
344              if (ixright < x[0]) ixright = x[0];
345              if (ixright > x[1]) ixright = x[1];
346
347              if ((alpha_left == 0.0 && alpha_right == 0.0) ||
348                  ixleft >= ixright) continue;
349
350              ixleft = pdv->image->width*(ixleft-c->bb.llx) / (c->bb.urx-c->bb.llx);
351              ixright = pdv->image->width*(ixright-c->bb.llx) / (c->bb.urx-c->bb.llx);
352
353              ileft  = ceil(ixleft);
354              iright = floor(ixright);
355
356              // output left corner points with alpha
357              if (ileft >= 1.0)
358                {
359                  double alpha = (ileft - ixleft) *
360                    (alpha_left*(xright-0.5*(ileft+xleft))+alpha_right*(0.5*(ileft+xleft)-xleft))/(xright-xleft);
361
362                  mat_6_mul(&src_point,imat,ileft-1.0,iscan);
363
364                  if (put_point_func(pdv->image,img,ileft-1.0,iscan,&src_point,alpha))
365                    return -1;
366
367#ifdef HPGS_PAINT_IMAGE_DEBUG
368                  hpgs_log("src: %lg,%lg, dest: %lg,%d.\n",
369                           src_point->x,src_point->y,ileft-1.0,iscan);
370#endif
371                }
372
373              // output intermediate pixels.
374              for (ii = ileft; ii < iright; ++ii)
375                {
376                  double alpha = (alpha_left*(xright-(ii+0.5))+alpha_right*((ii+0.5)-xleft))/(xright-xleft);
377
378                  mat_6_mul(&src_point,imat,ii,iscan);
379                  if (put_point_func(pdv->image,img,ii,iscan,&src_point,alpha))
380                    return -1;
381                }
382
383              // output right corner points with alpha
384              if (iright >= ileft && iright < pdv->image->width)
385                {
386                  double alpha = (ixright - iright) *
387                    (alpha_left*(xright-0.5*(iright+xright))+alpha_right*(0.5*(iright+xright)-xleft))/(xright-xleft);
388
389                  mat_6_mul(&src_point,imat,iright,iscan);
390
391                  if (put_point_func(pdv->image,img,iright,iscan,&src_point,alpha))
392                    return -1;
393
394#ifdef HPGS_PAINT_IMAGE_DEBUG
395                  hpgs_log("src: %lg,%lg, dest: %lg,%d.\n",
396                           src_point->x,src_point->y,iright,iscan);
397#endif
398                }
399            }
400        }
401      else
402        {
403          // go through clip segments - non-antialiased
404          for (io = 0; io+1<scanline->n_points; io+=2)
405            {
406              double xleft= scanline->points[io].x;
407              double xright= scanline->points[io+1].x;
408              double ileft,iright,ii;
409              hpgs_point src_point;
410
411              if (xleft < x[0]) xleft = x[0];
412              if (xleft > x[1]) xleft = x[1];
413
414              if (xright < x[0]) xright = x[0];
415              if (xright > x[1]) xright = x[1];
416
417              if (xleft >= xright) continue;
418
419              xleft = pdv->image->width*(xleft-c->bb.llx) / (c->bb.urx-c->bb.llx);
420              xright = pdv->image->width*(xright-c->bb.llx) / (c->bb.urx-c->bb.llx);
421
422              ileft  = ceil(xleft);
423              iright = floor(xright);
424
425              // output left corner points with alpha
426              if (ileft >= 1.0)
427                {
428                  double alpha = ileft - xleft;
429
430                  mat_6_mul(&src_point,imat,ileft-1.0,iscan);
431
432                  if (put_point_func(pdv->image,img,ileft-1.0,iscan,&src_point,alpha))
433                    return -1;
434
435#ifdef HPGS_PAINT_IMAGE_DEBUG
436                  hpgs_log("src: %lg,%lg, dest: %lg,%d.\n",
437                           src_point->x,src_point->y,ileft-1.0,iscan);
438#endif
439                }
440
441              // output intermediate pixels.
442              for (ii = ileft; ii < iright; ++ii)
443                {
444                  mat_6_mul(&src_point,imat,ii,iscan);
445                  if (put_point_func(pdv->image,img,ii,iscan,&src_point,1.0))
446                    return -1;
447                }
448
449              // output right corner points with alpha
450              if (iright >= ileft && iright < pdv->image->width)
451                {
452                  double alpha = xright - iright;
453
454                  mat_6_mul(&src_point,imat,iright,iscan);
455
456                  if (put_point_func(pdv->image,img,iright,iscan,&src_point,alpha))
457                    return -1;
458
459#ifdef HPGS_PAINT_IMAGE_DEBUG
460                  hpgs_log("src: %lg,%lg, dest: %lg,%d.\n",
461                           src_point->x,src_point->y,iright,iscan);
462#endif
463                }
464            }
465        }
466    }
467
468  return 0;
469}
470