Antialiased TTF LucasArts style speech? (didn't think so) [SOLVED]

Started by monkey0506, Sat 21/11/2009 21:02:53

Previous topic - Next topic

monkey0506

Okay so I searched in the forums and found this thread which linked to this tracker entry both of which are old.

My question is, has this changed? Is there a way to enable AA on a TTF font with LA style speech short of completely rendering the text myself. I ask coz it seems a bit funny that when the dialog options are displayed it works but the actual speech doesn't. :D

GarageGothic

#1
No, it's still not possible and you can't even truly work around it since text can't be rendered antialiased on a background-less GUI and DrawingSurface functions don't support alpha channels.

However, since you can now use normal script commands directly in your dialog script it HAS become a lot easier to create a custom Say function, and I wrote a bit of code that renders pretty smooth looking text by using an aliased outline and anti-aliased inner text. You can find the script in this thread, though unfortunately the example pics were on the americangirlscout server and are no longer working. I wrote a 2.72 compatible version for Tolworthy's Dante game, and he posted a screenshot of the script in action on his blog. Due to the lo-res coordinates limitation in 2.72 the outline in those shots is a few pixels thick, but the original script should render a crisp looking 1 pixel outline.

monkey0506

#2
Quote from: GarageGothic on Sun 22/11/2009 00:38:51DrawingSurface functions don't support alpha channels

That's not true. They just don't draw the alpha channel (that is the channel isn't copied, it is used) so if the surface is transparent then you get the pink haze. :P

Actually I encountered that exact problem with my custom dialog rendering, so I'm already doing this:

Code: ags
void DrawCharacter(this DrawingSurface*, Character *theCharacter) {
  if (theCharacter == null) return;
  ViewFrame *frame = Game.GetViewFrame(theCharacter.View, theCharacter.Loop, theCharacter.Frame);
  DynamicSprite *sprite;
  int graphic = frame.Graphic;
  if (frame.Flipped) {
    sprite = DynamicSprite.CreateFromExistingSprite(graphic, true);
    sprite.Flip(eFlipLeftToRight);
  }
  if (theCharacter.Scaling != 100) {
    int scale = theCharacter.Scaling;
    if (sprite == null) sprite = DynamicSprite.CreateFromExistingSprite(graphic, true);
    sprite.Resize((Game.SpriteWidth[graphic] * scale) / 100, (Game.SpriteHeight[graphic] * scale) / 100);
  }
  Region *rat = Region.GetAtRoomXY(theCharacter.x, theCharacter.y);
  if ((rat != null) && (rat != region[0]) && (rat.TintEnabled)) {
    if (sprite == null) sprite = DynamicSprite.CreateFromExistingSprite(graphic, true);
    sprite.Tint(rat.TintRed, rat.TintGreen, rat.TintBlue, rat.TintSaturation, 100);
  }
  if (sprite != null) graphic = sprite.Graphic;
  this.DrawImage(theCharacter.x - (Game.SpriteWidth[graphic] / 2) - GetViewportX(), theCharacter.y - Game.SpriteHeight[graphic] - theCharacter.z - GetViewportY(), graphic, theCharacter.Transparency);
  if (sprite != null) sprite.Delete();
}

void DrawObject(this DrawingSurface*, Object *theObject) {
  if ((theObject == null) || (!theObject.Graphic)) return;
  DynamicSprite *sprite;
  int graphic = theObject.Graphic;
  if (theObject.View) {
    ViewFrame *frame = Game.GetViewFrame(theObject.View, theObject.Loop, theObject.Frame);
    if (frame.Flipped) {
      sprite = DynamicSprite.CreateFromExistingSprite(frame.Graphic, true);
      sprite.Flip(eFlipLeftToRight);
    }
  }
  int scale = GetScalingAt(theObject.X, theObject.Y);
  if ((!theObject.IgnoreScaling) && (scale != 100)) {
    if (sprite == null) sprite = DynamicSprite.CreateFromExistingSprite(graphic, true);
    sprite.Resize((Game.SpriteWidth[graphic] * scale) / 100, (Game.SpriteHeight[graphic] * scale) / 100);
  }
  Region *rat = Region.GetAtRoomXY(theObject.X, theObject.Y);
  if ((rat != null) && (rat != region[0]) && (rat.TintEnabled)) {
    if (sprite == null) sprite = DynamicSprite.CreateFromExistingSprite(graphic, true);
    sprite.Tint(rat.TintRed, rat.TintGreen, rat.TintBlue, rat.TintSaturation, 100);
  }
  if (sprite != null) graphic = sprite.Graphic;
  this.DrawImage(theObject.X, theObject.Y - Game.SpriteHeight[graphic], graphic, theObject.Transparency);
  if (sprite != null) sprite.Delete();
}

function dialog_options_render(DialogOptionsRenderingInfo *info) {
  // Clear the area
  info.Surface.Clear();
  // AGS doesn't currently support alpha channeled DrawingSurface transparency. This should flatten it
  DynamicSprite *sprite = DynamicSprite.CreateFromBackground();
  DrawingSurface *surface = sprite.GetDrawingSurface();
  int i = 0;
  while ((i < Game.CharacterCount) || (i < Room.ObjectCount)) {
    if ((i < Game.CharacterCount) && (character[i].Room == player.Room)) surface.DrawCharacter(character[i]);
    if (i < Room.ObjectCount) {
      surface.DrawObject(object[i]);
      if (object[i].Graphic) {
        int scale = GetScalingAt(object[i].X, object[i].Y);
        int ow = (Game.SpriteWidth[object[i].Graphic] * scale) / 100;
        int oh = (Game.SpriteHeight[object[i].Graphic] * scale) / 100;
        if (object[i].IgnoreScaling) {
          ow = Game.SpriteWidth[object[i].Graphic];
          oh = Game.SpriteHeight[object[i].Graphic];
        }
        int ox1 = object[i].X;
        int ox2 = ox1 + ow;
        int j = 0;
        while (j < Game.CharacterCount) {
          if (character[j].Room == player.Room) {
            ViewFrame *frame = Game.GetViewFrame(character[j].View, character[j].Loop, character[j].Frame);
            int cw = (Game.SpriteWidth[frame.Graphic] * character[j].Scaling) / 100;
            int cx1 = character[j].x - (cw / 2);
            if ((((cx1 + cw) >= ox1) && (cx1 <= ox2)) && (character[j].y > object[i].Baseline)) surface.DrawCharacter(character[j]);
          }
          j++;
        }
      }
    }
    i++;
  }
  surface.Release();
  sprite.Crop(info.X, info.Y, info.Width, info.Height);
  info.Surface.DrawImage(0, 0, sprite.Graphic);
  sprite.Delete();
  i = 1;
  int ypos = 0;
  // Render all the options that are enabled
  // ...
}


Basically I have to take a copy of the background, merge in all the characters and objects, crop the image, and then draw the dialog options on top of that. The reason I can't use a screenshot is because it would catch the mouse cursor as it moves around, and if the text changed (i.e. scrolling, turning an option on/off) it would catch the old text as well.

So basically if you want to get AA text with LA style speech you could do it by finding the bounding box for the text and applying the same process.

I just didn't know if there was an easier way. I'm already discovering just how much work actually goes into these game things. :=

Oh and though this does take into account flipped frames, it blatantly ignores z-orders, baselines, scaling, etc. So if those are important to you, you may want to revise the code appropriately. ;) I've modified the code now so it respects character z-orders, baselines, scaling, and transparency. It respects region tinting but I can't think of a way to use character, object, or ambient tinting.

GarageGothic

QuoteThat's not true. They just don't draw the alpha channel (that is the channel isn't copied, it is used) so if the surface is transparent then you get the pink haze.  :P

Oh, you're such a know-it-all  :P

How is that script working out for you? I wrote a similar function for my refraction module, not because of the cursor but just because CreateFromScreenShot is so goddamn slow in Direct3D mode and I need to call it from repeatedly_execute. And it did implement z-order, tinting, scaling etc., but I found the main problem was that there's no way of supporting walkbehinds, so I have to be real careful of where I place my refraction objects on the screen. I would imagine this to be an even larger problems when used for dialogs that can be run virtually anywhere on the screen?

monkey0506

#4
Quote from: GarageGothic on Sun 22/11/2009 11:45:37
QuoteThat's not true. They just don't draw the alpha channel (that is the channel isn't copied, it is used) so if the surface is transparent then you get the pink haze.  :P

Oh, you're such a know-it-all  :P

Hence the gratuitous sticking-my-tongue-out-in-your-general-direction action! :=

Quote from: GarageGothic on Sun 22/11/2009 11:45:37How is that script working out for you? I wrote a similar function...but I found the main problem was that there's no way of supporting walkbehinds, so I have to be real careful of where I place my refraction objects on the screen. I would imagine this to be an even larger problems when used for dialogs that can be run virtually anywhere on the screen?

The script is working out well for what I'm using it for. The reason it doesn't support scaling, z-orders, and the like is quite simply because I'm not using any of those! ;D

As for walk-behinds those are completely overrated. Use an Object or Character instead. C'mon people! How else are you gonna get it AA-smooth for your high-res game. 8)

Edit: Also (overuse of smileys aside) it seems to me that the custom dialog rendering isn't updated every single game loop (like rep_ex(_always) would do) but rather just when the mouse cursor has moved over the area covered by the DialogOptionsRenderingInfo object. So that can only help the performance here.

SMF spam blocked by CleanTalk