chiark / gitweb /
Do not mention bpp in mustfail1 since we check it ourselves nowadays
[ypp-sc-tools.web-live.git] / pctb / structure.c
index da80cb9fda86297b74e57f8e6a37ec7a887244bd..859c7d91d9fa3f3e0b8722aed2f942ed7c8ec190 100644 (file)
@@ -51,7 +51,12 @@ char *archipelago, *island;
 #define OTHERCOORD_x y
 #define OTHERCOORD_y x
 
-const CanonColourInfo canoncolourinfos[]= {
+typedef struct {
+  Rgb rgb; /* on screen */
+  char c; /* canonical */
+} CanonColourInfo;
+
+const CanonColourInfo canoncolourinfo_table[]= {
   { 0x475A5E, '*' }, /* edge */
   { 0x2C5F7A, '*' }, /* edge just under box heading shadow */
   { 0xC5C7AE, '*' }, /* blank area of partial commodities list */
@@ -89,6 +94,31 @@ const CanonColourInfo canoncolourinfos[]= {
   { 0,0 }
 };
 
+CanonColourInfoReds canoncolourinfo_tree;
+
+void canon_colour_prepare(void) {
+  const CanonColourInfo *cci;
+  for (cci=canoncolourinfo_table; cci->c; cci++) {
+    unsigned char r= cci->rgb >> 16;
+    unsigned char g= cci->rgb >>  8;
+    unsigned char b= cci->rgb;
+
+    CanonColourInfoGreens *greens= canoncolourinfo_tree.red2[r];
+    if (!greens) {
+      greens= canoncolourinfo_tree.red2[r]= mmalloc(sizeof(*greens));
+      FILLZERO(*greens);
+    }
+
+    CanonColourInfoBlues *blues= greens->green2[g];
+    if (!blues) {
+      blues= greens->green2[g]= mmalloc(sizeof(*blues));
+      memset(blues, '?', sizeof(blues));
+    }
+
+    blues->blue2[b]= cci->c;
+  }
+}
+
 
 static void mustfail1(const char *file, int line, const char *what) {
   fprintf(stderr,
@@ -96,10 +126,8 @@ static void mustfail1(const char *file, int line, const char *what) {
  "Unable to figure out contents of YPP client display.\n"
  "Please check the following:\n"
  "   * YPP client is showing commodity listing screen\n"
- "   * YPP client has antialiased text turned off (Ye / Options / General)\n"
  "   * YPP client window is on top (we try to raise it but your window\n"
  "      manager might have prevented that from succeeding)\n"
- "   * Your X display is 24bpp (NB some VNC servers use 16bpp by default)\n"
  "\n"
  "If all of these are true, please report this as a fault.\n\n"
          "Technical details:"
@@ -127,7 +155,7 @@ static void mustfail2(void) {
 #define MRGB(v) fprintf(stderr," %s=%06lx", #v,(v))
 #define MC(v) fprintf(stderr," %s='%c'", #v,(v))
 #define MS(v) fprintf(stderr," %s=\"%s\"", #v,(v))
-#define MF(v) fprintf(stderr," %s=\"%f\"", #v,(v))
+#define MF(v) fprintf(stderr," %s=%f", #v,(v))
 #define MSB(v) fprintf(stderr," %s", (v))
 #define MR(v) fprintf(stderr," %s=%d,%d..%d,%d",\
                       #v,(v).tl.x,(v).tl.y,(v).br.x,(v).br.y)
@@ -211,20 +239,20 @@ static int commod_selector_matches(Rect search, const char *const *all,
          );                                            \
   }while(0)
 
-#define ADJUST_BOX(search,insidechrs,want, lim,LIMIT_MUST, TLBR,XY,increm) \
-  for (;;) {                                                              \
-    LIMIT_MUST( (search).tl.XY != (search).br.XY &&                       \
-               (search).tl.XY != (lim),                                   \
-               MR((search));MSB(#TLBR);MSB(#XY) );                        \
-    int got=0;                                                            \
-    Point p=(search).tl;                                                  \
-    for (p.XY=(search).TLBR.XY;                                                   \
-        p.OTHERCOORD_##XY <= (search).br.OTHERCOORD_##XY;                 \
-        p.OTHERCOORD_##XY++)                                              \
-      got += !!strchr(insidechrs, get_p(p));                              \
-    if (got >= (want))                                                    \
-      break;                                                              \
-    (search).TLBR.XY += increm;                                                   \
+#define ADJUST_BOX(search,insidechrs,OP,want, lim,LIMIT_MUST, TLBR,XY,increm) \
+  for (;;) {                                                                 \
+    LIMIT_MUST( (search).tl.XY != (search).br.XY &&                          \
+               (search).tl.XY != (lim),                                      \
+               MR((search));MSB(#TLBR);MSB(#XY) );                           \
+    int got=0;                                                               \
+    Point p=(search).tl;                                                     \
+    for (p.XY=(search).TLBR.XY;                                                      \
+        p.OTHERCOORD_##XY <= (search).br.OTHERCOORD_##XY;                    \
+        p.OTHERCOORD_##XY++)                                                 \
+      got += !!strchr(insidechrs, get_p(p));                                 \
+    if ((got) OP (want))                                                     \
+      break;                                                                 \
+    (search).TLBR.XY += increm;                                                      \
   }
 
 void find_structure(CanonImage *im, int *max_relevant_y_r) {
@@ -327,8 +355,8 @@ void find_structure(CanonImage *im, int *max_relevant_y_r) {
 void check_correct_commodities(void) {
   Rect search= { { 50,39 }, { 130,59 } };
 
-  ADJUST_BOX(search,"_",10, cim->h, MUST, tl,y,+1);
-  ADJUST_BOX(search,"_",10, 0,      MUST, br,y,-1);
+  ADJUST_BOX(search,"_",>=,10, cim->h, MUST, tl,y,+1);
+  ADJUST_BOX(search,"_",>=,10, 0,      MUST, br,y,-1);
 
   debug_rect("commodselr",1, search);
 
@@ -388,18 +416,16 @@ static void file_read_image_ppm(FILE *f) {
   if (!(inpam.maxval == 255 &&
        inpam.bytes_per_sample == 1 &&
        inpam.format == RPPM_FORMAT))
-    fatal("PNM screenshot(s) file must be 8bpp 1 byte per sample RGB");
+    fatal("PNM screenshot(s) file must be 8bpp 1 byte-per-sample RGB raw");
 
   CANONICALISE_IMAGE(im, inpam.width, inpam.height, {
-    int r= fread(&rgb_buf,1,3,f);
+    int rr= fread(&rgb_buf,1,3,f);
     sysassert(!ferror(f));
-    if (r!=3) fatal("PNM screenshot(s) file ends unexpectedly");
-
-    rgb=
-       ((unsigned long)rgb_buf[0]<<16) |
-       ((unsigned long)rgb_buf[1]<<8) |
-                      (rgb_buf[2]);
+    if (rr!=3) fatal("PNM screenshot(s) file ends unexpectedly");
 
+    r= rgb_buf[0];
+    g= rgb_buf[1];
+    b= rgb_buf[2];
   });
 
   sysassert(!ferror(screenshot_file));
@@ -437,6 +463,44 @@ void read_screenshots(void) {
   progress_log("read %d screenshots.",npages);
 }
 
+static double find_aa_density(const RgbImage *ri, Point p, long background,
+                             long foreground, int fg_extra) {
+  Rgb here= ri_rgb(ri, p.x, p.y);
+
+  double alpha[3], alpha_mean=0;
+  int i;
+  for (i=0; i<3; i++) {
+    unsigned char here_chan= here       >> (i*8);
+    unsigned char bg_chan=   background >> (i*8);
+    unsigned char fg_chan=   foreground >> (i*8);
+    double alpha_chan=
+      ((double)here_chan    - (double)bg_chan) /
+      ((fg_chan + fg_extra) - (double)bg_chan);
+    alpha[i]= alpha_chan;
+    alpha_mean += alpha_chan * (1/3.0);
+  }
+
+  double thresh= 1.5/AAMAXVAL;
+  double alpha_min= alpha_mean - thresh;
+  double alpha_max= alpha_mean + thresh;
+  for (i=0; i<3; i++)
+    MUST( alpha_min <= alpha[i] && alpha[i] <= alpha_max,
+         MP(p);
+         MRGB(here);MRGB(background);MRGB(foreground);MI(fg_extra);
+         MF(alpha_min); MI(i);MF(alpha[i]);MF(alpha_max) );
+
+  if (   -1e-5 <  alpha_mean && alpha_mean <= 0.0     ) alpha_mean= 0.0;
+  if (1.0      <= alpha_mean && alpha_mean <= 1.0+1e-5) alpha_mean= 1.0;
+
+  MUST( 0 <= alpha_mean &&
+       (fg_extra ? (alpha_mean < 0.999) : (alpha_mean <= 1.0)),
+       MP(p);
+       MRGB(here);MRGB(background);MRGB(foreground);MI(fg_extra);
+       MF(alpha_mean); MF(alpha[0]);MF(alpha[1]);MF(alpha[2]); );
+
+  return alpha_mean;
+}
+
 static void find_commodity(int offset, Rect *rr) {
   /* rr->tl.x==-1 if offset out of range */
   rr->tl.y= commbasey - offset*comminty;
@@ -462,57 +526,45 @@ static void find_table_entry(Rect commod, int colno, Rect *cell) {
   const RgbImage *ri= cim->rgb;
   
   Rgb background= ri_rgb(ri, cell->br.x, cell->br.y);
-  long bg_count=0, white_count=0, black_count=0;
+  long bg_count=0, light_count=0, dark_count=0;
   Point p;
   FOR_P_RECT(p,*cell) {
     Rgb here= ri_rgb(ri, p.x, p.y);
     if (here == background) bg_count++;
-    else if (here == 0) black_count++;
-    else if (here == 0xffffffU) white_count++;
+    else if (here < background) dark_count++;
+    else if (here > background) light_count++;
   }
   long total_count= RECT_W(*cell) * RECT_H(*cell);
-  MUST( bg_count > 8*total_count / 10,
-       MR(*cell);MIL(bg_count);MIL(white_count);MIL(black_count) );
+  MUST( bg_count > total_count / 2,
+       MR(*cell);MIL(total_count);MIL(bg_count);
+       MIL(light_count);MIL(dark_count) );
   if (bg_count == total_count)
     return;
 
-  MUST( !!black_count != !!white_count,
-       MR(*cell);MIL(bg_count);MIL(white_count);MIL(black_count) );
+  MUST( !!dark_count != !!light_count,
+       MR(*cell);MIL(total_count);MIL(bg_count);
+       MIL(light_count);MIL(dark_count) );
 
-  debugf("TABLEENTRY col=%d %d,%d..%d,%d bg=%ld white=%ld black=%ld\n",
+  debugf("TABLEENTRY col=%d %d,%d..%d,%d bg=%ld light=%ld dark=%ld\n",
         colno, cell->tl.x,cell->tl.y, cell->br.x,cell->br.y,
-        bg_count, white_count, black_count);
+        bg_count, light_count, dark_count);
   
-  Rgb foreground= white_count ? 0xffffffU : 0;
+  Rgb foreground;
+  double fg_extra;
+  if (light_count) {
+    foreground= 0xffffffU;
+    fg_extra= +1;
+  } else {
+    foreground= 0;
+    fg_extra= -1;
+  }
   int monochrome= 1;
 
   FOR_P_RECT(p,*cell) {
-    Rgb here= ri_rgb(ri, p.x, p.y);
-    double alpha[3], alpha_mean=0;
-    int i;
-    for (i=0; i<3; i++) {
-      unsigned char here_chan= here       >> (i*8);
-      unsigned char bg_chan=   background >> (i*8);
-      unsigned char fg_chan=   foreground >> (i*8);
-      double alpha_chan=
-       ((double)here_chan - (double)bg_chan) /
-       ((double)fg_chan   - (double)bg_chan);
-      alpha[i]= alpha_chan;
-      alpha_mean += alpha_chan * (1/3.0);
-    }
+    double alpha= find_aa_density(ri,p,background,foreground,fg_extra);
 
-    double thresh= 1.0/AAMAXVAL;
-    double alpha_min= alpha_mean - thresh;
-    double alpha_max= alpha_mean + thresh;
-    for (i=0; i<3; i++)
-      MUST( alpha_min <= alpha[i] && alpha[i] <= alpha_max,
-           MI(i);MRGB(here);MRGB(background);MRGB(foreground);
-           MF(alpha_min);MF(alpha[i]);MF(alpha_max) );
-
-    MUST( 0 <= alpha_mean && alpha_mean <= 1,
-         MRGB(here);MRGB(background);MRGB(foreground);
-         MF(alpha_mean) );
-    int here_int= AAMAXVAL*alpha_mean;
+    int here_int= floor((AAMAXVAL+1)*alpha);
+    assert(here_int <= AAMAXVAL);
     if (!(here_int==0 || here_int==AAMAXVAL)) monochrome=0;
     cim->d[p.y * cim->w + p.x]= '0' + here_int;
   }
@@ -598,15 +650,15 @@ Rect find_sunshine_widget(void) {
   sunshiner.tl.y= 227;
   sunshiner.br.y= 228;
 
-  ADJUST_BOX(sunshiner,"o*",30, 100,MUST, tl,y,-1);
-  ADJUST_BOX(sunshiner,"o*",30, 100,MUST, br,y,+1);
+  ADJUST_BOX(sunshiner,"o*",>=,30, 100,MUST, tl,y,-1);
+  ADJUST_BOX(sunshiner,"o*",>=,30, 100,MUST, br,y,+1);
   debug_rect("sunshiner",0, sunshiner);
 
   MUST(sunshiner.br.y - sunshiner.tl.y > 20, MR(sunshiner));
   sunshiner.br.y--;
 
-  ADJUST_BOX(sunshiner,"o",20, (cim->w - 1034 + 700), MUST, tl,x,-1);
-  ADJUST_BOX(sunshiner,"o",20,  cim->w,               MUST, br,x,+1);
+  ADJUST_BOX(sunshiner,"o",>=,20, (cim->w - 1034 + 700), MUST, tl,x,-1);
+  ADJUST_BOX(sunshiner,"o",>=,20,  cim->w,               MUST, br,x,+1);
   debug_rect("sunshiner",1, sunshiner);
   return sunshiner;
 }
@@ -637,11 +689,11 @@ void find_islandname(RgbImage *ri) {
     islandnamer.tl.y=                 128;
     islandnamer.br.y=                 156;
 
-    ADJUST_BOX(islandnamer,"o",5, 0,      MUST, tl,y,+1);
-    ADJUST_BOX(islandnamer,"o",5, cim->h, MUST, br,y,-1);
+    ADJUST_BOX(islandnamer,"o",>=,5, 0,      MUST, tl,y,+1);
+    ADJUST_BOX(islandnamer,"o",>=,5, cim->h, MUST, br,y,-1);
 
-    ADJUST_BOX(islandnamer,"o",1, 0,      MUST, tl,x,+1);
-    ADJUST_BOX(islandnamer,"o",1, cim->w, MUST, br,x,-1);
+    ADJUST_BOX(islandnamer,"o",>=,1, 0,      MUST, tl,x,+1);
+    ADJUST_BOX(islandnamer,"o",>=,1, cim->w, MUST, br,x,-1);
 
     debug_rect("islandnamer",0, islandnamer);
 //    int larger_islandnamebry= islandnamer.tl.y + 25;
@@ -661,7 +713,7 @@ void find_islandname(RgbImage *ri) {
       }
 
     identify_rgbimage(ri, islandnamer, archisland, "island");
-  } else {
+  } else if (!strcmp(sunshine,"Land - Ahoy!")) {
     Rect islandnamer;
 
     islandnamer.tl.x= (sunshiner.tl.x + sunshiner.br.x) / 2;
@@ -674,23 +726,28 @@ void find_islandname(RgbImage *ri) {
     WALK_UNTIL_MUST(islandnamer.br,x, +1, cim->w,         'o');
     debug_rect("islandnamer",__LINE__, islandnamer);
 
-    require_rectangle_r(islandnamer, "O*", __LINE__);
+#define RW (RECT_W(islandnamer))
+#define RH (RECT_H(islandnamer))
 
-    int rw= RECT_W(islandnamer);
-    ADJUST_BOX(islandnamer,"O",rw-4, cim->h, MUST,br,y,+1);
+    ADJUST_BOX(islandnamer,"O",>=,RW-4, cim->h, MUST,br,y,+1);
     debug_rect("islandnamer",__LINE__, islandnamer);
 
     islandnamer.br.y += 2;
-    ADJUST_BOX(islandnamer,"O",1,    cim->h, MUST,br,y,+1);
+
+    ADJUST_BOX(islandnamer,"*",<,RW, cim->h, MUST,br,y,+1);
     debug_rect("islandnamer",__LINE__, islandnamer);
 
     islandnamer.tl.y= islandnamer.br.y-1;
     islandnamer.br.y= islandnamer.br.y+1;
-    ADJUST_BOX(islandnamer,"*",rw,   cim->h, MUST,br,y,+1);
-    ADJUST_BOX(islandnamer,"O",1,    cim->w, MUST,tl,x,+1);
     debug_rect("islandnamer",__LINE__, islandnamer);
 
-    MUST( RECT_H(islandnamer) <= 31, MR(islandnamer));
+    ADJUST_BOX(islandnamer,"*",>=,RW, cim->h, MUST,br,y,+1);
+    debug_rect("islandnamer",__LINE__, islandnamer);
+
+    ADJUST_BOX(islandnamer,"*",<, RH, cim->w, MUST,tl,x,+1);
+    debug_rect("islandnamer",__LINE__, islandnamer);
+
+    MUST( RECT_H(islandnamer) <= 30, MR(islandnamer));
 
     Point p;
     int nspaces=1, might_be_colon=0;
@@ -707,10 +764,15 @@ void find_islandname(RgbImage *ri) {
       
       for (p.y=islandnamer.tl.y; p.y<=islandnamer.br.y; p.y++) {
        pattern <<= 1;
-       switch (get_p(p)) {
-       case 'O': runs[nruns]++;                         pattern |= 1u; break;
-       case '*': if (runs[nruns]) { nruns++; runs[nruns]=0; }          break;
-       default: abort();
+       double alpha= find_aa_density(ri,p, 0xCCCCAA,0x002255,0);
+       if (alpha >= 0.49) {
+          runs[nruns]++;
+          pattern |= 1u;
+       } else {
+         if (runs[nruns]) {
+           nruns++;
+           runs[nruns]=0;
+         }
        }
       }
 
@@ -724,7 +786,7 @@ void find_islandname(RgbImage *ri) {
        nspaces++;
        might_be_colon=0;
       } else {
-       if (nruns==2 && runs[1]==runs[0]) {
+       if (!(pattern & 1UL<<31) && nruns==2 && runs[1]==runs[0]) {
          if (!nspaces) {
            if (pattern==colon_pattern)
              goto ok_might_be_colon;
@@ -747,6 +809,10 @@ void find_islandname(RgbImage *ri) {
     islandnamer.br.x= p.x;
 
     identify_rgbimage(ri, islandnamer, archisland, "island");
+  } else {
+
+    MUST(!"sunshine shows ship or ahoy", MS(sunshine) );
+
   }
 
   char *delim= strstr(archisland," - ");