• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**********************************************************************
2  * File:        coutln.c  (Formerly coutline.c)
3  * Description: Code for the C_OUTLINE class.
4  * Author:                  Ray Smith
5  * Created:                 Mon Oct 07 16:01:57 BST 1991
6  *
7  * (C) Copyright 1991, Hewlett-Packard Ltd.
8  ** Licensed under the Apache License, Version 2.0 (the "License");
9  ** you may not use this file except in compliance with the License.
10  ** You may obtain a copy of the License at
11  ** http://www.apache.org/licenses/LICENSE-2.0
12  ** Unless required by applicable law or agreed to in writing, software
13  ** distributed under the License is distributed on an "AS IS" BASIS,
14  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  ** See the License for the specific language governing permissions and
16  ** limitations under the License.
17  *
18  **********************************************************************/
19 
20 #include "mfcpch.h"
21 #include          <string.h>
22 #ifdef __UNIX__
23 #include          <assert.h>
24 #endif
25 #include          "coutln.h"
26 
27 ELISTIZE_S (C_OUTLINE)
28 ICOORD C_OUTLINE::step_coords[4] = {
29   ICOORD (-1, 0), ICOORD (0, -1), ICOORD (1, 0), ICOORD (0, 1)
30 };
31 
32 /**********************************************************************
33  * C_OUTLINE::C_OUTLINE
34  *
35  * Constructor to build a C_OUTLINE from a CRACKEDGE LOOP.
36  **********************************************************************/
37 
C_OUTLINE(CRACKEDGE * startpt,ICOORD bot_left,ICOORD top_right,inT16 length)38 C_OUTLINE::C_OUTLINE (
39 //constructor
40 CRACKEDGE * startpt,             //outline to convert
41 ICOORD bot_left,                 //bounding box
42 ICOORD top_right, inT16 length   //length of loop
43 ):box (bot_left, top_right), start (startpt->pos) {
44   inT16 stepindex;               //index to step
45   CRACKEDGE *edgept;             //current point
46 
47   stepcount = length;            //no of steps
48   if (length == 0) {
49     steps = NULL;
50     return;
51   }
52                                  //get memory
53   steps = (uinT8 *) alloc_mem (step_mem());
54   memset(steps, 0, step_mem());
55   edgept = startpt;
56 
57   for (stepindex = 0; stepindex < length; stepindex++) {
58                                  //set compact step
59     set_step (stepindex, edgept->stepdir);
60     edgept = edgept->next;
61   }
62 }
63 
64 
65 /**********************************************************************
66  * C_OUTLINE::C_OUTLINE
67  *
68  * Constructor to build a C_OUTLINE from a C_OUTLINE_FRAG.
69  **********************************************************************/
C_OUTLINE(ICOORD startpt,DIR128 * new_steps,inT16 length)70 C_OUTLINE::C_OUTLINE (
71 //constructor
72                                  //steps to copy
73 ICOORD startpt, DIR128 * new_steps,
74 inT16 length                     //length of loop
75 ):start (startpt) {
76   inT8 dirdiff;                  //direction difference
77   DIR128 prevdir;                //previous direction
78   DIR128 dir;                    //current direction
79   DIR128 lastdir;                //dir of last step
80   TBOX new_box;                   //easy bounding
81   inT16 stepindex;               //index to step
82   inT16 srcindex;                //source steps
83   ICOORD pos;                    //current position
84 
85   pos = startpt;
86   stepcount = length;            //no of steps
87                                  //get memory
88   steps = (uinT8 *) alloc_mem (step_mem());
89   memset(steps, 0, step_mem());
90 
91   lastdir = new_steps[length - 1];
92   prevdir = lastdir;
93   for (stepindex = 0, srcindex = 0; srcindex < length;
94   stepindex++, srcindex++) {
95     new_box = TBOX (pos, pos);
96     box += new_box;
97                                  //copy steps
98     dir = new_steps[srcindex];
99     set_step(stepindex, dir);
100     dirdiff = dir - prevdir;
101     pos += step (stepindex);
102     if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) {
103       stepindex -= 2;            //cancel there-and-back
104       prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir;
105     }
106     else
107       prevdir = dir;
108   }
109   ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ());
110   do {
111     dirdiff = step_dir (stepindex - 1) - step_dir (0);
112     if (dirdiff == 64 || dirdiff == -64) {
113       start += step (0);
114       stepindex -= 2;            //cancel there-and-back
115       for (int i = 0; i < stepindex; ++i)
116         set_step(i, step_dir(i + 1));
117     }
118   }
119   while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64));
120   stepcount = stepindex;
121   ASSERT_HOST (stepcount >= 4);
122 }
123 
124 /**********************************************************************
125  * C_OUTLINE::C_OUTLINE
126  *
127  * Constructor to build a C_OUTLINE from a rotation of a C_OUTLINE.
128  **********************************************************************/
129 
C_OUTLINE(C_OUTLINE * srcline,FCOORD rotation)130 C_OUTLINE::C_OUTLINE(                     //constructor
131                      C_OUTLINE *srcline,  //outline to
132                      FCOORD rotation      //rotate
133                     ) {
134   TBOX new_box;                   //easy bounding
135   inT16 stepindex;               //index to step
136   inT16 dirdiff;                 //direction change
137   ICOORD pos;                    //current position
138   ICOORD prevpos;                //previous dest point
139 
140   ICOORD destpos;                //destination point
141   inT16 destindex;               //index to step
142   DIR128 dir;                    //coded direction
143   uinT8 new_step;
144 
145   stepcount = srcline->stepcount * 2;
146   if (stepcount == 0) {
147     steps = NULL;
148     box = srcline->box;
149     box.rotate(rotation);
150     return;
151   }
152                                  //get memory
153   steps = (uinT8 *) alloc_mem (step_mem());
154   memset(steps, 0, step_mem());
155 
156   for (int iteration = 0; iteration < 2; ++iteration) {
157     DIR128 round1 = iteration == 0 ? 32 : 0;
158     DIR128 round2 = iteration != 0 ? 32 : 0;
159     pos = srcline->start;
160     prevpos = pos;
161     prevpos.rotate (rotation);
162     start = prevpos;
163     box = TBOX (start, start);
164     destindex = 0;
165     for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) {
166       pos += srcline->step (stepindex);
167       destpos = pos;
168       destpos.rotate (rotation);
169       //  printf("%i %i %i %i ", destpos.x(), destpos.y(), pos.x(), pos.y());
170       while (destpos.x () != prevpos.x () || destpos.y () != prevpos.y ()) {
171         dir = DIR128 (FCOORD (destpos - prevpos));
172         dir += 64;                 //turn to step style
173         new_step = dir.get_dir ();
174         //  printf(" %i\n", new_step);
175         if (new_step & 31) {
176           set_step(destindex++, dir + round1);
177           prevpos += step(destindex - 1);
178           if (destindex < 2
179             || ((dirdiff =
180             step_dir (destindex - 1) - step_dir (destindex - 2)) !=
181             -64 && dirdiff != 64)) {
182             set_step(destindex++, dir + round2);
183             prevpos += step(destindex - 1);
184           } else {
185             prevpos -= step(destindex - 1);
186             destindex--;
187             prevpos -= step(destindex - 1);
188             set_step(destindex - 1, dir + round2);
189             prevpos += step(destindex - 1);
190           }
191         }
192         else {
193           set_step(destindex++, dir);
194           prevpos += step(destindex - 1);
195         }
196         while (destindex >= 2 &&
197                ((dirdiff =
198                  step_dir (destindex - 1) - step_dir (destindex - 2)) == -64 ||
199                 dirdiff == 64)) {
200           prevpos -= step(destindex - 1);
201           prevpos -= step(destindex - 2);
202           destindex -= 2;        // Forget u turn
203         }
204         //ASSERT_HOST(prevpos.x() == destpos.x() && prevpos.y() == destpos.y());
205         new_box = TBOX (destpos, destpos);
206         box += new_box;
207       }
208     }
209     ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ());
210     dirdiff = step_dir (destindex - 1) - step_dir (0);
211     while ((dirdiff == 64 || dirdiff == -64) && destindex > 1) {
212       start += step (0);
213       destindex -= 2;
214       for (int i = 0; i < destindex; ++i)
215         set_step(i, step_dir(i + 1));
216       dirdiff = step_dir (destindex - 1) - step_dir (0);
217     }
218     if (destindex >= 4)
219       break;
220   }
221   ASSERT_HOST(destindex <= stepcount);
222   stepcount = destindex;
223   destpos = start;
224   for (stepindex = 0; stepindex < stepcount; stepindex++) {
225     destpos += step (stepindex);
226   }
227   ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ());
228 }
229 
230 // Build a fake outline, given just a bounding box and append to the list.
FakeOutline(const TBOX & box,C_OUTLINE_LIST * outlines)231 void C_OUTLINE::FakeOutline(const TBOX& box, C_OUTLINE_LIST* outlines) {
232   C_OUTLINE_IT ol_it(outlines);
233   // Make a C_OUTLINE from the bounds. This is a bit of a hack,
234   // as there is no outline, just a bounding box, but it works nicely.
235   CRACKEDGE start;
236   start.pos = box.topleft();
237   C_OUTLINE* outline = new C_OUTLINE(&start, box.topleft(), box.botright(), 0);
238   ol_it.add_to_end(outline);
239 }
240 
241 /**********************************************************************
242  * C_OUTLINE::area
243  *
244  * Compute the area of the outline.
245  **********************************************************************/
246 
area()247 inT32 C_OUTLINE::area() {  //winding number
248   int stepindex;                 //current step
249   inT32 total_steps;             //steps to do
250   inT32 total;                   //total area
251   ICOORD pos;                    //position of point
252   ICOORD next_step;              //step to next pix
253   C_OUTLINE_IT it = child ();
254 
255   pos = start_pos ();
256   total_steps = pathlength ();
257   total = 0;
258   for (stepindex = 0; stepindex < total_steps; stepindex++) {
259                                  //all intersected
260     next_step = step (stepindex);
261     if (next_step.x () < 0)
262       total += pos.y ();
263     else if (next_step.x () > 0)
264       total -= pos.y ();
265     pos += next_step;
266   }
267   for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
268     total += it.data ()->area ();//add areas of children
269 
270   return total;
271 }
272 
273 /**********************************************************************
274  * C_OUTLINE::perimeter
275  *
276  * Compute the perimeter of the outline and its first level children.
277  **********************************************************************/
278 
perimeter()279 inT32 C_OUTLINE::perimeter() {
280   inT32 total_steps;             // Return value.
281   C_OUTLINE_IT it = child();
282 
283   total_steps = pathlength();
284   for (it.mark_cycle_pt(); !it.cycled_list(); it.forward())
285     total_steps += it.data()->pathlength();  // Add perimeters of children.
286 
287   return total_steps;
288 }
289 
290 
291 /**********************************************************************
292  * C_OUTLINE::outer_area
293  *
294  * Compute the area of the outline.
295  **********************************************************************/
296 
outer_area()297 inT32 C_OUTLINE::outer_area() {  //winding number
298   int stepindex;                 //current step
299   inT32 total_steps;             //steps to do
300   inT32 total;                   //total area
301   ICOORD pos;                    //position of point
302   ICOORD next_step;              //step to next pix
303 
304   pos = start_pos ();
305   total_steps = pathlength ();
306   if (total_steps == 0)
307     return box.area();
308   total = 0;
309   for (stepindex = 0; stepindex < total_steps; stepindex++) {
310                                  //all intersected
311     next_step = step (stepindex);
312     if (next_step.x () < 0)
313       total += pos.y ();
314     else if (next_step.x () > 0)
315       total -= pos.y ();
316     pos += next_step;
317   }
318 
319   return total;
320 }
321 
322 
323 /**********************************************************************
324  * C_OUTLINE::count_transitions
325  *
326  * Compute the number of x and y maxes and mins in the outline.
327  **********************************************************************/
328 
count_transitions(inT32 threshold)329 inT32 C_OUTLINE::count_transitions(                 //winding number
330                                    inT32 threshold  //on size
331                                   ) {
332   BOOL8 first_was_max_x;         //what was first
333   BOOL8 first_was_max_y;
334   BOOL8 looking_for_max_x;       //what is next
335   BOOL8 looking_for_min_x;
336   BOOL8 looking_for_max_y;       //what is next
337   BOOL8 looking_for_min_y;
338   int stepindex;                 //current step
339   inT32 total_steps;             //steps to do
340                                  //current limits
341   inT32 max_x, min_x, max_y, min_y;
342   inT32 initial_x, initial_y;    //initial limits
343   inT32 total;                   //total changes
344   ICOORD pos;                    //position of point
345   ICOORD next_step;              //step to next pix
346 
347   pos = start_pos ();
348   total_steps = pathlength ();
349   total = 0;
350   max_x = min_x = pos.x ();
351   max_y = min_y = pos.y ();
352   looking_for_max_x = TRUE;
353   looking_for_min_x = TRUE;
354   looking_for_max_y = TRUE;
355   looking_for_min_y = TRUE;
356   first_was_max_x = FALSE;
357   first_was_max_y = FALSE;
358   initial_x = pos.x ();
359   initial_y = pos.y ();          //stop uninit warning
360   for (stepindex = 0; stepindex < total_steps; stepindex++) {
361                                  //all intersected
362     next_step = step (stepindex);
363     pos += next_step;
364     if (next_step.x () < 0) {
365       if (looking_for_max_x && pos.x () < min_x)
366         min_x = pos.x ();
367       if (looking_for_min_x && max_x - pos.x () > threshold) {
368         if (looking_for_max_x) {
369           initial_x = max_x;
370           first_was_max_x = FALSE;
371         }
372         total++;
373         looking_for_max_x = TRUE;
374         looking_for_min_x = FALSE;
375         min_x = pos.x ();        //reset min
376       }
377     }
378     else if (next_step.x () > 0) {
379       if (looking_for_min_x && pos.x () > max_x)
380         max_x = pos.x ();
381       if (looking_for_max_x && pos.x () - min_x > threshold) {
382         if (looking_for_min_x) {
383           initial_x = min_x;     //remember first min
384           first_was_max_x = TRUE;
385         }
386         total++;
387         looking_for_max_x = FALSE;
388         looking_for_min_x = TRUE;
389         max_x = pos.x ();
390       }
391     }
392     else if (next_step.y () < 0) {
393       if (looking_for_max_y && pos.y () < min_y)
394         min_y = pos.y ();
395       if (looking_for_min_y && max_y - pos.y () > threshold) {
396         if (looking_for_max_y) {
397           initial_y = max_y;     //remember first max
398           first_was_max_y = FALSE;
399         }
400         total++;
401         looking_for_max_y = TRUE;
402         looking_for_min_y = FALSE;
403         min_y = pos.y ();        //reset min
404       }
405     }
406     else {
407       if (looking_for_min_y && pos.y () > max_y)
408         max_y = pos.y ();
409       if (looking_for_max_y && pos.y () - min_y > threshold) {
410         if (looking_for_min_y) {
411           initial_y = min_y;     //remember first min
412           first_was_max_y = TRUE;
413         }
414         total++;
415         looking_for_max_y = FALSE;
416         looking_for_min_y = TRUE;
417         max_y = pos.y ();
418       }
419     }
420 
421   }
422   if (first_was_max_x && looking_for_min_x) {
423     if (max_x - initial_x > threshold)
424       total++;
425     else
426       total--;
427   }
428   else if (!first_was_max_x && looking_for_max_x) {
429     if (initial_x - min_x > threshold)
430       total++;
431     else
432       total--;
433   }
434   if (first_was_max_y && looking_for_min_y) {
435     if (max_y - initial_y > threshold)
436       total++;
437     else
438       total--;
439   }
440   else if (!first_was_max_y && looking_for_max_y) {
441     if (initial_y - min_y > threshold)
442       total++;
443     else
444       total--;
445   }
446 
447   return total;
448 }
449 
450 
451 /**********************************************************************
452  * C_OUTLINE::operator<
453  *
454  * Return TRUE if the left operand is inside the right one.
455  **********************************************************************/
456 
457 BOOL8
operator <(const C_OUTLINE & other) const458 C_OUTLINE::operator< (           //winding number
459 const C_OUTLINE & other          //other outline
460 ) const
461 {
462   inT16 count = 0;               //winding count
463   ICOORD pos;                    //position of point
464   inT32 stepindex;               //index to cstep
465 
466   if (!box.overlap (other.box))
467     return FALSE;                //can't be contained
468   if (stepcount == 0)
469     return other.box.contains(this->box);
470 
471   pos = start;
472   for (stepindex = 0; stepindex < stepcount
473     && (count = other.winding_number (pos)) == INTERSECTING; stepindex++)
474     pos += step (stepindex);     //try all points
475   if (count == INTERSECTING) {
476                                  //all intersected
477     pos = other.start;
478     for (stepindex = 0; stepindex < other.stepcount
479       && (count = winding_number (pos)) == INTERSECTING; stepindex++)
480                                  //try other way round
481       pos += other.step (stepindex);
482     return count == INTERSECTING || count == 0;
483   }
484   return count != 0;
485 }
486 
487 
488 /**********************************************************************
489  * C_OUTLINE::winding_number
490  *
491  * Return the winding number of the outline around the given point.
492  **********************************************************************/
493 
winding_number(ICOORD point) const494 inT16 C_OUTLINE::winding_number(              //winding number
495                                 ICOORD point  //point to wind around
496                                ) const {
497   inT16 stepindex;               //index to cstep
498   inT16 count;                   //winding count
499   ICOORD vec;                    //to current point
500   ICOORD stepvec;                //step vector
501   inT32 cross;                   //cross product
502 
503   vec = start - point;           //vector to it
504   count = 0;
505   for (stepindex = 0; stepindex < stepcount; stepindex++) {
506     stepvec = step (stepindex);  //get the step
507                                  //crossing the line
508     if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) {
509       cross = vec * stepvec;     //cross product
510       if (cross > 0)
511         count++;                 //crossing right half
512       else if (cross == 0)
513         return INTERSECTING;     //going through point
514     }
515     else if (vec.y () > 0 && vec.y () + stepvec.y () <= 0) {
516       cross = vec * stepvec;
517       if (cross < 0)
518         count--;                 //crossing back
519       else if (cross == 0)
520         return INTERSECTING;     //illegal
521     }
522     vec += stepvec;              //sum vectors
523   }
524   return count;                  //winding number
525 }
526 
527 
528 /**********************************************************************
529  * C_OUTLINE::turn_direction
530  *
531  * Return the sum direction delta of the outline.
532  **********************************************************************/
533 
turn_direction() const534 inT16 C_OUTLINE::turn_direction() const {  //winding number
535   DIR128 prevdir;                //previous direction
536   DIR128 dir;                    //current direction
537   inT16 stepindex;               //index to cstep
538   inT8 dirdiff;                  //direction difference
539   inT16 count;                   //winding count
540 
541   if (stepcount == 0)
542     return 128;
543   count = 0;
544   prevdir = step_dir (stepcount - 1);
545   for (stepindex = 0; stepindex < stepcount; stepindex++) {
546     dir = step_dir (stepindex);
547     dirdiff = dir - prevdir;
548     ASSERT_HOST (dirdiff == 0 || dirdiff == 32 || dirdiff == -32);
549     count += dirdiff;
550     prevdir = dir;
551   }
552   ASSERT_HOST (count == 128 || count == -128);
553   return count;                  //winding number
554 }
555 
556 
557 /**********************************************************************
558  * C_OUTLINE::reverse
559  *
560  * Reverse the direction of an outline.
561  **********************************************************************/
562 
reverse()563 void C_OUTLINE::reverse() {  //reverse drection
564   DIR128 halfturn = MODULUS / 2; //amount to shift
565   DIR128 stepdir;                //direction of step
566   inT16 stepindex;               //index to cstep
567   inT16 farindex;                //index to other side
568   inT16 halfsteps;               //half of stepcount
569 
570   halfsteps = (stepcount + 1) / 2;
571   for (stepindex = 0; stepindex < halfsteps; stepindex++) {
572     farindex = stepcount - stepindex - 1;
573     stepdir = step_dir (stepindex);
574     set_step (stepindex, step_dir (farindex) + halfturn);
575     set_step (farindex, stepdir + halfturn);
576   }
577 }
578 
579 
580 /**********************************************************************
581  * C_OUTLINE::move
582  *
583  * Move C_OUTLINE by vector
584  **********************************************************************/
585 
move(const ICOORD vec)586 void C_OUTLINE::move(                  // reposition OUTLINE
587                      const ICOORD vec  // by vector
588                     ) {
589   C_OUTLINE_IT it(&children);  // iterator
590 
591   box.move (vec);
592   start += vec;
593 
594   for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
595     it.data ()->move (vec);      // move child outlines
596 }
597 
598 // If this outline is smaller than the given min_size, delete this and
599 // remove from its list, via *it, after checking that *it points to this.
600 // Otherwise, if any children of this are too small, delete them.
601 // On entry, *it must be an iterator pointing to this. If this gets deleted
602 // then this is extracted from *it, so an iteration can continue.
RemoveSmallRecursive(int min_size,C_OUTLINE_IT * it)603 void C_OUTLINE::RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it) {
604   if (box.width() < min_size || box.height() < min_size) {
605     ASSERT_HOST(this == it->data());
606     delete it->extract();  // Too small so get rid of it and any children.
607   } else if (!children.empty()) {
608     // Search the children of this, deleting any that are too small.
609     C_OUTLINE_IT child_it(&children);
610     for (child_it.mark_cycle_pt(); !child_it.cycled_list();
611          child_it.forward()) {
612       C_OUTLINE* child = child_it.data();
613       child->RemoveSmallRecursive(min_size, &child_it);
614     }
615   }
616 }
617 
618 /**********************************************************************
619  * C_OUTLINE::plot
620  *
621  * Draw the outline in the given colour.
622  **********************************************************************/
623 
624 #ifndef GRAPHICS_DISABLED
plot(ScrollView * window,ScrollView::Color colour) const625 void C_OUTLINE::plot(                //draw it
626                      ScrollView* window,  //window to draw in
627                      ScrollView::Color colour   //colour to draw in
628                     ) const {
629   inT16 stepindex;               //index to cstep
630   ICOORD pos;                    //current position
631   DIR128 stepdir;                //direction of step
632   DIR128 oldstepdir;             //previous stepdir
633 
634   pos = start;                   //current position
635   window->Pen(colour);
636   if (stepcount == 0) {
637     window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
638     return;
639   }
640   window->SetCursor(pos.x(), pos.y());
641 
642   stepindex = 0;
643   stepdir = step_dir (0);        //get direction
644   while (stepindex < stepcount) {
645     do {
646       pos += step (stepindex);   //step to next
647       stepindex++;               //count steps
648       oldstepdir = stepdir;
649                                  //new direction
650       stepdir = step_dir (stepindex);
651     }
652     while (stepindex < stepcount
653       && oldstepdir.get_dir () == stepdir.get_dir ());
654     //merge straight lines
655      window->DrawTo(pos.x(), pos.y());
656   }
657 }
658 #endif
659 
660 
661 /**********************************************************************
662  * C_OUTLINE::operator=
663  *
664  * Assignment - deep copy data
665  **********************************************************************/
666 
667                                  //assignment
operator =(const C_OUTLINE & source)668 C_OUTLINE & C_OUTLINE::operator= (
669 const C_OUTLINE & source         //from this
670 ) {
671   box = source.box;
672   start = source.start;
673   if (steps != NULL)
674     free_mem(steps);
675   stepcount = source.stepcount;
676   steps = (uinT8 *) alloc_mem (step_mem());
677   memmove (steps, source.steps, step_mem());
678   if (!children.empty ())
679     children.clear ();
680   children.deep_copy(&source.children, &deep_copy);
681   return *this;
682 }
683 
chain_step(int chaindir)684 ICOORD C_OUTLINE::chain_step(int chaindir) {
685   return step_coords[chaindir % 4];
686 }
687