SFML community forums

Help => Graphics => Topic started by: T.T.H. on January 30, 2008, 10:57:17 pm

Title: subpixel accuracy of sprite of image
Post by: T.T.H. on January 30, 2008, 10:57:17 pm
Hi there

Since I am a nitpicker regarding pixel moving I stumbled upon a problem: when I create a sprite from an image and draw that sprite on the screen, the border pixel of the image are only half as big as the inner pixel of the image.

...and now with pictures:

Source image, 32 x 32 pixel, with colorful 1 pixel wide border and checkerboard within:

(http://www.teeteehaa.de/SFML/image_1x1.png)

Screenshot, rendered with SFML, sprite scaled quite large and rendered four times around the center of the screen:

(http://www.teeteehaa.de/SFML/sprite_subpixel_accuracy.png)

As you can see the colorful border pixel are only half as big as the black and white checkerboard pixel.

Relevant piece of code:
Code: [Select]

  // render window
  sf::RenderWindow MyRenderWindow(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "SFML", sf::Style::Close);
  MyRenderWindow.OptimizeForNonOpenGL(true);
  MyRenderWindow.SetBackgroundColor(sf::Color(255, 0, 255));

  // image
  sf::Image MyImage;
  MyImage.LoadFromFile(IMAGE_FILE);
  MyImage.SetSmooth(false);

  // sprite
  sf::Sprite MySprite(MyImage);
  MySprite.Scale(20.f, 20.f);

  // center of screen
  float CenterX = (float) MyRenderWindow.GetWidth()  / 2.0f;
  float CenterY = (float) MyRenderWindow.GetHeight() / 2.0f;

  // big loop
  while (Looping)
  {
    // draw top left sprite
    MySprite.SetPosition(CenterX - MySprite.GetWidth(), CenterY - MySprite.GetHeight());
    MyRenderWindow.Draw(MySprite);

    // draw top right sprite
    MySprite.SetPosition(CenterX                      , CenterY - MySprite.GetHeight());
    MyRenderWindow.Draw(MySprite);

    // draw bottom left sprite
    MySprite.SetPosition(CenterX - MySprite.GetWidth(), CenterY                       );
    MyRenderWindow.Draw(MySprite);

    // draw bottom right sprite
    MySprite.SetPosition(CenterX                      , CenterY                       );
    MyRenderWindow.Draw(MySprite);

    // display screen
    MyRenderWindow.Display();


I don't like that and I would like to change that. Unfortunately I'm not sure whether it's due to SFML or due to OpenGL or due to the fact that I am totally missing something. Please somebody enlighten me  :D
Title: subpixel accuracy of sprite of image
Post by: Laurent on January 31, 2008, 02:34:51 am
Weird, that's not supposed to happen. Which version of SMFL are you using ?
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on January 31, 2008, 09:33:16 pm
I was using revision 408. I now updated to revision 442 but the result is exactly the same.

Any other info I could provide? Do you want the whole VC2005 project of my test application?
Title: subpixel accuracy of sprite of image
Post by: Laurent on February 01, 2008, 02:47:24 am
Quote
Do you want the whole VC2005 project of my test application?

Yep, I'll try it at home.
Title: subpixel accuracy of sprite of image
Post by: Laurent on February 03, 2008, 09:56:54 am
The problem seems to come from the high scale. Using a bigger image and applying no scale works just fine. I guess it has to do with the OpenGL filtering / rasterization algorithm, combined with my texture coordinate trick (subtracting a half-texel to make the OpenGL coordinates match the screen ones).
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on February 04, 2008, 08:06:30 pm
So whenever I "zoom in" on a sprite by giving it any scale over 1.0 the border of the sprite looses its "pixel preciseness"? Sorry to say, but I'd consider that a bummer :shock:

For something I am tinkering on I do have a seamless grass texture being 256 x 256 pixel in size. My application "tiles" that texture to create a grass landscape and can zoom in and out from it. I stumbled about that pixel un-precision because I noticed "edges" between my perfectly fine seamless textures when I zoomed in on my terrain, not only at "zoom deep in" but at any kind of "zoom in".
Title: subpixel accuracy of sprite of image
Post by: Laurent on February 05, 2008, 02:13:03 am
Quote
So whenever I "zoom in" on a sprite by giving it any scale over 1.0 the border of the sprite looses its "pixel preciseness"? Sorry to say, but I'd consider that a bummer

I wouldn't say that, I think it will produce this kind of issue only with high scales.

Anyway, I don't think we can avoid this when using scale or zoom, as the scene units become different from screen pixels ; you just can't get a perfect mapping.
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on February 06, 2008, 11:34:23 pm
Quote from: "Laurent"
I wouldn't say that, I think it will produce this kind of issue only with high scales.

No. It happens with any scale larger than 1.0.

Quote from: "Laurent"
Anyway, I don't think we can avoid this when using scale or zoom

:cry:


While trying that again out I even stumbled about the next problem (sooorry), this time related to image smoothing.


Original image - 32x32 pixel, drawn 4 times around center of screen, image smoothing is enabled:

(http://www.teeteehaa.de/SFML/checkerboard_1x1.png)

Scale factor 1.0 - please note those nasty "circles", where do those come from?!?
Might they come from the "high contrast" checkerboard plus some fancy Moire patterns?
But at a scale factor of 1.0 everything should be copied exactly, shouldn't it?
(sidenote: image is reduced to 256 colors, but even in full color mode those circles are clearly visible)

(http://www.teeteehaa.de/SFML/resize_checkerboard_smooth_1_0.png)

Scale factor 1.1 - please note the "circles" and please even note the "half size border pixel":

(http://www.teeteehaa.de/SFML/resize_checkerboard_smooth_1_1.png)

Scale factor 1.5 - including "half size border pixel":

(http://www.teeteehaa.de/SFML/resize_checkerboard_smooth_1_5.png)

Scale factor 2.0 - including "circles" (even while not that visible) and "half size border pixel":

(http://www.teeteehaa.de/SFML/resize_checkerboard_smooth_2_0.png)

Scale factor 0.9 - "circles":

(http://www.teeteehaa.de/SFML/resize_checkerboard_smooth_0_9.png)

Scale factor 0.5 - total bogus?!

(http://www.teeteehaa.de/SFML/resize_checkerboard_smooth_0_5.png)


SFML revision 449. Help.


Code: [Select]
// --- includes ---

// windows
#include <windows.h>

// SFML
#include "SFML/Graphics.hpp"

// STL
#include <iostream>

// --- defines ---

#define IMAGE_FILE "checkerboard_1x1.bmp"
// #define IMAGE_FILE "image_1x1.bmp"
const int SCREEN_WIDTH = 200;
const int SCREEN_HEIGHT = 200;
const float SCALE_FACTOR = 1.0f;
const bool IMAGE_SMOOTHING = true;

// --- signal handler ---

BOOL WINAPI MyHandlerRoutine(DWORD dwCtrlType)
{
  std::cout << "exiting the hard way" << std::endl;
  exit(0);
  return TRUE;
}

// --- run ---

bool runSFML()
{
  // variables
  bool Looping(true);
  sf::Event MyEvent;

  // render window
  sf::RenderWindow MyRenderWindow(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "SFML", sf::Style::Close);
  MyRenderWindow.OptimizeForNonOpenGL(true);
  MyRenderWindow.SetBackgroundColor(sf::Color(255, 0, 255));

  // image
  sf::Image MyImage;
  if (!MyImage.LoadFromFile(IMAGE_FILE))
  {
    std::cout << "failed to load image '" << IMAGE_FILE << "'" << std::endl;
    return false;
  }
  MyImage.SetRepeat(true);
  MyImage.SetSmooth(IMAGE_SMOOTHING);

  // sprite
  sf::Sprite MySprite(MyImage);
  MySprite.Scale(SCALE_FACTOR, SCALE_FACTOR);

  // center of screen
  float CenterX = (float) MyRenderWindow.GetWidth()  / 2.0f;
  float CenterY = (float) MyRenderWindow.GetHeight() / 2.0f;

  // big loop
  while (Looping)
  {
    // draw top left sprite
    MySprite.SetPosition(CenterX - MySprite.GetWidth(), CenterY - MySprite.GetHeight());
    MyRenderWindow.Draw(MySprite);

    // draw top right sprite
    MySprite.SetPosition(CenterX                      , CenterY - MySprite.GetHeight());
    MyRenderWindow.Draw(MySprite);

    // draw bottom left sprite
    MySprite.SetPosition(CenterX - MySprite.GetWidth(), CenterY                       );
    MyRenderWindow.Draw(MySprite);

    // draw bottom right sprite
    MySprite.SetPosition(CenterX                      , CenterY                       );
    MyRenderWindow.Draw(MySprite);

    // display screen
    MyRenderWindow.Display();

    // poll events
    while (MyRenderWindow.GetEvent(MyEvent))
    {
      // check whether window was closed
      if (MyEvent.Type == sf::Event::Closed)
      {
        Looping = false;
      }

      // check whether escape was pressed
      if ((MyEvent.Type == sf::Event::KeyReleased) && (MyEvent.Key.Code == sf::Key::Escape))
      {
        Looping = false;
      }
    }
  }

  return true;
}

// --- main ---

int main(int argc, char *argv[])
{
  // add signal handler routine
  if (!SetConsoleCtrlHandler(MyHandlerRoutine, TRUE))
  {
    std::cout << "failed to add signal handler routine" << std::endl;
    exit(1);
  }

  // start
  std::cout << "started" << std::endl;

  // run SFML
  runSFML();

  // stopped
  std::cout << "stopped" << std::endl;

  return 0;
}
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on February 06, 2008, 11:55:38 pm
...and another post that I am not just nitpicking in checkerboard stress tests but actually do notice that effects on "real life data" alias "I want to draw a landscape made of grass tiles":

Original image - which tiles wonderfully seamless (http://www.teeteehaa.de/SFML/grass.html):

(http://www.teeteehaa.de/SFML/grass_256x256.png)

Scale factor 1.0 - when looking close I can see an "edge":

(http://www.teeteehaa.de/SFML/resize_grass_smooth_1_0.png)

Scale factor 2.0 - I obviously do see an "edge":

(http://www.teeteehaa.de/SFML/resize_grass_smooth_2_0.png)

Those "edges"  get even more visible when I scroll around over a landscape made of those tiles.
Title: subpixel accuracy of sprite of image
Post by: Laurent on February 07, 2008, 01:58:34 am
Good demonstration of the problem ;)

I'll see what I can do.
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on February 07, 2008, 08:12:49 pm
Ha! After tinkering around in Sprite.cpp and changing line 184 in Sprite::Render from...
Code: [Select]
// Calculate the texture coordinates
FloatRect TexCoords = myImage->GetTexCoords(mySubRect);

...into...
Code: [Select]
// Calculate the texture coordinates
FloatRect TexCoords = myImage->GetTexCoords(mySubRect, false);

...and by so basically disabling your "half texel texture coordinate trick" I now get the following results:

Checkerboard 1.0:

(http://www.teeteehaa.de/SFML/resize_checkerboard_smooth_unadjusted_1_0.png)

Checkerboard 1.5:

(http://www.teeteehaa.de/SFML/resize_checkerboard_smooth_unadjusted_1_5.png)

Checkerboard 2.0:

(http://www.teeteehaa.de/SFML/resize_checkerboard_smooth_unadjusted_2_0.png)

Checkerboard 0.5:

(http://www.teeteehaa.de/SFML/resize_checkerboard_smooth_unadjusted_0_5.png)

Grass 2.0:

(http://www.teeteehaa.de/SFML/resize_grass_smooth_unadjusted_2_0.png)

 :D  :D  :D

This leads me to the following conclusions:

My original "half border pixel" problem is caused (or at least affected) by your "texture coordinate trick" and by disabling that "trick" I exactly totally 100% get what I want (pixel perfect seamless landscapes). On the other hand I do not know why you initially put that "trick" into the code. But maybe you give that reason a second thought (please).

My following "strangle circles appearing" problem seems to be caused by some Moire patterns on my "extreme contrast checkerboard" image in combination with the "not pixel precise" usage of the image. When the pixel precision is done correctly the circles simply disappear.
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on February 07, 2008, 09:24:14 pm
P.S.: are the values in the following code snippet casting in a correct way so that no precision is lost? Personally I'd put several (float) in front of the Rect.Left, Rect.Top, Rect.Right and Rect.Bottom because int + float = int in my opinion and I'd prefer to have (float) int + float = float.
Code: [Select]
FloatRect Image::GetTexCoords(const IntRect& Rect, bool Adjust) const
{
    float Offset = Adjust ? 0.5f : 0.f;
    return FloatRect((Rect.Left   + Offset) / myTextureWidth,
                     (Rect.Top    + Offset) / myTextureHeight,
                     (Rect.Right  - Offset) / myTextureWidth,
                     (Rect.Bottom - Offset) / myTextureHeight);
}
Title: subpixel accuracy of sprite of image
Post by: Laurent on February 08, 2008, 05:16:51 am
Quote
On the other hand I do not know why you initially put that "trick" into the code. But maybe you give that reason a second thought (please).

OpenGL texels doesn't map exactly to the screen pixels, they are offseted by half a pixel. If I don't use this trick, I get unwanted pixels on the borders of images with smoothing activated. But I think I could just use the trick when smoothing is enabled.

Quote
P.S.: are the values in the following code snippet casting in a correct way so that no precision is lost? Personally I'd put several (float) in front of the Rect.Left, Rect.Top, Rect.Right and Rect.Bottom because int + float = int in my opinion and I'd prefer to have (float) int + float = float.

int + float = float.
If there's at least one float as an operand, the result of any operator will be a float.
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on February 08, 2008, 09:05:44 am
Quote from: "Laurent"
If I don't use this trick, I get unwanted pixels on the borders of images with smoothing activated.

Isn't that what GL_REPEAT and GL_CLAMP are for? (assuming the sprite is made of the whole image).

In case you have one image and make several sprites out of it, yes, not using the trick will include unwanted pixel of the neighbor sprite. To be honest that even is my full intention: I put my grass tile 25 times into an image (5x5) and make a sprite out of the center tile to achieve a correct smoothing "to the neighbor" and so a perfectly seamless landscape. If it would be that single sprite only I could use GL_REPEAT, too, but in those 24 tiles around the center are several "borders" to create "round borders of grass". And for those "half grass, half empty" tiles I indeed don't want any neighbor pixel included on an "empty" edge - so my source image is GL_CLAMP.

Finally a suggestion:

Code: [Select]
////////////////////////////////////////////////////////////
/// Enable or disable several image options:
/// Repeat: how to define pixels outside the texture range
/// Smooth: image smoothing filter
/// AdjustTexel: prevent pixel outside the sprite range being
///              used when smoothing filter is enabled
////////////////////////////////////////////////////////////
void Image::SetOptions(bool Repeat, bool Smooth, bool AdjustTexel)
{
    // Remember options
    myRepeat = Repeat;
    mySmooth = Smooth;
    myAdjustTexel = AdjustTexel;

    if (myGLTexture)
    {
        // Change OpenGL texture wrap mode
        GLint PreviousTexture;
        GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &PreviousTexture));
        GLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
        GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, Repeat ? GL_REPEAT : GL_CLAMP));
        GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, Repeat ? GL_REPEAT : GL_CLAMP));
        GLCheck(glBindTexture(GL_TEXTURE_2D, PreviousTexture));

        // Change OpenGL texture filter
        GLint PreviousTexture;
        GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &PreviousTexture));
        GLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
        GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Smooth ? GL_LINEAR : GL_NEAREST));
        GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Smooth ? GL_LINEAR : GL_NEAREST));
        GLCheck(glBindTexture(GL_TEXTURE_2D, PreviousTexture));
    }
}

////////////////////////////////////////////////////////////
/// Convert a subrect expressed in pixels, into float
/// texture coordinates
////////////////////////////////////////////////////////////
FloatRect Image::GetTexCoords(const IntRect& Rect) const
{
    float Offset = mySmooth && myAdjustTexel ? 0.5f : 0.f;
    return FloatRect((Rect.Left   + Offset) / myTextureWidth,
                     (Rect.Top    + Offset) / myTextureHeight,
                     (Rect.Right  - Offset) / myTextureWidth,
                     (Rect.Bottom - Offset) / myTextureHeight);
}

////////////////////////////////////////////////////////////
/// Create the OpenGL texture
////////////////////////////////////////////////////////////
bool Image::CreateTexture()
{
    // [...snip...]

    // Create the OpenGL texture
    GLCheck(glGenTextures(1, (GLuint*)&myGLTexture));
    GLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
    GLCheck(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, myTextureWidth, myTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL));

    // Set texture options
    SetOptions(bool myRepeat, bool mySmooth, bool myAdjustTexel)

    // Copy the pixel buffer to the OpenGL texture
    Update();

    // Put back the previous texture
    GLCheck(glBindTexture(GL_TEXTURE_2D, PreviousTexture));

    return true;
}
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on March 10, 2008, 10:54:17 pm
(note: late answer because I was in holidays)

Updating to 492 I've seen you changed something in 474:
Quote from: "svn log"
Removed Image::SetRepeat (useless and even incorrect when texture was padded with extra pixels)
Removed the "half-pixel trick" on texture coordinates when an image is not smooth

Unfortunately my problem still exists: there is a visible edge between my seamless tiles.


To solve my problem (and doing so without hacking around inside SFML itself) I need a way to control the "half-pixel trick" myself (via some SFML API). Currently this is not possible because Sprite::Render calls...
Code: [Select]
// Calculate the texture coordinates
// FloatRect TexCoords = myImage->GetTexCoords(mySubRect);

...and in Image.hpp...
Code: [Select]
FloatRect GetTexCoords(const IntRect& Rect, bool Adjust = true) const;
...the default value for the parameter Adjust is true.


In addition I'd like to get back an API to set the "repetition" of images or at least to have the default value GL_REPEAT instead of GL_CLAMP for the setting GL_TEXTURE_WRAP of images. You are even saying yourself about GL_CLAMP...
Quote
...(useless and even incorrect when texture was padded with extra pixels)



Yes, I could make both those changes locally in my very own version of SFML, but a) I'd prefer to have an API for it and b) I'm probably not the only person wishing pixel preciseness.
Title: subpixel accuracy of sprite of image
Post by: Laurent on March 11, 2008, 02:31:29 am
Quote
the default value for the parameter Adjust is true.

But now, if the image is not smooth it won't apply. Isn't it what you suggested previously ?

Quote
In addition I'd like to get back an API to set the "repetition" of images or at least to have the default value GL_REPEAT instead of GL_CLAMP for the setting GL_TEXTURE_WRAP of images.

The quote was about GL_REPEAT, not GL_CLAMP. I can't put it back, because it wouldn't be consistent. Imagine the texture is padded because its dimensions are not power of two : you would never get the result you expect with GL_REPEAT.
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on March 12, 2008, 09:55:08 pm
Quote from: "Laurent"
I can't put it back, because it wouldn't be consistent. Imagine the texture is padded because its dimensions are not power of two : you would never get the result you expect with GL_REPEAT.

Hmm, good point.

Quote from: "Laurent"
But now, if the image is not smooth it won't apply. Isn't it what you suggested previously?

No. I do need Smoothing. Actually I do not care at all about un-smoothed images.


Okay, again, as precise as I am able to describe it:

When image smoothing is off there is no need for the half-pixel-trick because OpenGL does not take any pixel outside your source area of the sprite into account.

When image smoothing is on there might be need for the half-pixel-trick:

There is need for the half-pixel-trick when you do not want OpenGL taking any pixel outside the source area into account but you can live with some distortion of the source area (-0.5 pixel on each side of the source area).

There must not be any half-pixel-trick in case there must not be any distortion, e.x. when you try to make 100% seamless tiles e.x. for a terrain where a distortion would be visible as some kind of "edge" between the (smoothed) images of the adjacent tiles (look closely here (http://www.teeteehaa.de/SFML/resize_grass_smooth_2_0.png)). In that same case the user himself has to remember that OpenGL will take pixel outside the source area into account (+0.5 pixel on each side of the source area) and so the user has to make his own preparations therefore, e.x. putting 3x3 the very same tile into one image and picking only the center tile as source area for the sprite.


Thinking of my upcoming birthday next Saturday and hypothetically assuming that I have a wish free I'd wish for the following API in SFML regarding my personal needs of my own application:
Code: [Select]

void Image::SetSmooth(bool Smooth, bool Adjust)
{
    myIsSmooth = Smooth;
    myIsAdjust = Adjust;

    if (myGLTexture)
    {
        // Change OpenGL texture filter
        GLint PreviousTexture;
        GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &PreviousTexture));
        GLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
        GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Smooth ? GL_LINEAR : GL_NEAREST));
        GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Smooth ? GL_LINEAR : GL_NEAREST));
        GLCheck(glBindTexture(GL_TEXTURE_2D, PreviousTexture));
    }
}

FloatRect Image::GetTexCoords(const IntRect& Rect) const
{
    float Width  = static_cast<float>(myTextureWidth);
    float Height = static_cast<float>(myTextureHeight);

    if (myIsSmooth && myIsAdjust)
    {
        return FloatRect((Rect.Left   + 0.5f) / Width,
                         (Rect.Top    + 0.5f) / Height,
                         (Rect.Right  - 0.5f) / Width,
                         (Rect.Bottom - 0.5f) / Height);
    }
    else
    {
        return FloatRect(Rect.Left   / Width,
                         Rect.Top    / Height,
                         Rect.Right  / Width,
                         Rect.Bottom / Height);
    }
}

Basically Adjust is no more function parameter "decided" by sf::Sprite (and other SFML classes) when calling sf::Image::GetTexCoords but it is a member variable of sf::Image itself, accessible by the user. In that way it's 1) coupled directly with the Smooth setting to which it refers anyway and 2) it can be controlled by the user (me) depending on the requirements of the user's application.
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on November 26, 2008, 11:02:17 pm
I am digging up this old thread because this issue still is a problem for me and because I want to present a solution how I "fixed" it for myself. The change is made in SFML directly as it's not possible to fix within my own application but the change is pretty small, pretty simple and -in my opinion- pretty helpful for people like me who desire/demand pixel-preciseness even during antialiased/smoothed rendering. So without further ado I will now post a patch against r936 of SFML. Just copy&paste the following text into a file called "subpixelaccuracy.patch" and apply it to SFML - under Windows simply with TortoiseSVN and "Apply patch..." or under Linux with the diff command (if I remember correctly).

Laurent, I'd really like to see this implemented in SFML and I tried to make it as small and as easy and as reasonable for you as possible. In case you are not convinced of its necessity yet I can give you an example application clearly demonstrating the whole issue.

Code: [Select]
Index: include/SFML/Graphics/Image.hpp
===================================================================
--- include/SFML/Graphics/Image.hpp (revision 936)
+++ include/SFML/Graphics/Image.hpp (working copy)
@@ -224,6 +224,16 @@
     void SetSmooth(bool Smooth);
 
     ////////////////////////////////////////////////////////////
+    /// Enable or disable half-texel adjustment.
+    /// This parameter is enabled by default
+    /// and only relevant if Smooth is enabled, too
+    ///
+    /// \param Adjust : True to enable half-texel adjustment, false to disable it
+    ///
+    ////////////////////////////////////////////////////////////
+    void SetAdjust(bool Adjust);
+
+    ////////////////////////////////////////////////////////////
     /// Return the width of the image
     ///
     /// \return Width in pixels
@@ -248,6 +258,14 @@
     bool IsSmooth() const;
 
     ////////////////////////////////////////////////////////////
+    /// Tells whether the half-texel adjustment is enabled or not
+    ///
+    /// \return True if half-texel adjustment is enabled
+    ///
+    ////////////////////////////////////////////////////////////
+    bool IsAdjust() const;
+
+    ////////////////////////////////////////////////////////////
     /// Convert a subrect expressed in pixels, into float
     /// texture coordinates
     ///
@@ -324,6 +342,7 @@
     unsigned int               myTextureHeight;     ///< Actual texture height (can be greater than image height because of padding)
     unsigned int               myTexture;           ///< Internal texture identifier
     bool                       myIsSmooth;          ///< Status of the smooth filter
+    bool                       myIsAdjust;          ///< Status of the texel adjustment
     mutable std::vector<Color> myPixels;            ///< Pixels of the image
     mutable bool               myNeedTextureUpdate; ///< Status of synchronization between pixels in central memory and the internal texture un video memory
     mutable bool               myNeedArrayUpdate;   ///< Status of synchronization between pixels in central memory and the internal texture un video memory
Index: src/SFML/Graphics/Image.cpp
===================================================================
--- src/SFML/Graphics/Image.cpp (revision 936)
+++ src/SFML/Graphics/Image.cpp (working copy)
@@ -47,6 +47,7 @@
 myTextureHeight    (0),
 myTexture          (0),
 myIsSmooth         (true),
+myIsAdjust         (true),
 myNeedTextureUpdate(false),
 myNeedArrayUpdate  (false)
 {
@@ -65,6 +66,7 @@
 myTextureHeight    (Copy.myTextureHeight),
 myTexture          (0),
 myIsSmooth         (Copy.myIsSmooth),
+myIsAdjust         (Copy.myIsAdjust),
 myPixels           (Copy.myPixels),
 myNeedTextureUpdate(false),
 myNeedArrayUpdate  (false)
@@ -83,6 +85,7 @@
 myTextureHeight    (0),
 myTexture          (0),
 myIsSmooth         (true),
+myIsAdjust         (true),
 myNeedTextureUpdate(false),
 myNeedArrayUpdate  (false)
 {
@@ -100,6 +103,7 @@
 myTextureHeight    (0),
 myTexture          (0),
 myIsSmooth         (true),
+myIsAdjust         (true),
 myNeedTextureUpdate(false),
 myNeedArrayUpdate  (false)
 {
@@ -483,6 +487,15 @@
 
 
 ////////////////////////////////////////////////////////////
+/// Enable or disable half-texel adjustment
+////////////////////////////////////////////////////////////
+void Image::SetAdjust(bool Adjust)
+{
+    myIsAdjust = Adjust;
+}
+
+
+////////////////////////////////////////////////////////////
 /// Return the width of the image
 ////////////////////////////////////////////////////////////
 unsigned int Image::GetWidth() const
@@ -510,6 +523,14 @@
 
 
 ////////////////////////////////////////////////////////////
+/// Tells whether the half-texel adjustment is enabled or not
+////////////////////////////////////////////////////////////
+bool Image::IsAdjust() const
+{
+    return myIsAdjust;
+}
+
+////////////////////////////////////////////////////////////
 /// Convert a subrect expressed in pixels, into float
 /// texture coordinates
 ////////////////////////////////////////////////////////////
@@ -518,7 +539,7 @@
     float Width  = static_cast<float>(myTextureWidth);
     float Height = static_cast<float>(myTextureHeight);
 
-    if (Adjust && myIsSmooth)
+    if (Adjust && myIsAdjust && myIsSmooth)
     {
         return FloatRect((Rect.Left   + 0.5f) / Width,
                          (Rect.Top    + 0.5f) / Height,
@@ -573,6 +594,7 @@
     std::swap(myTextureHeight,     Temp.myTextureHeight);
     std::swap(myTexture,           Temp.myTexture);
     std::swap(myIsSmooth,          Temp.myIsSmooth);
+    std::swap(myIsAdjust,          Temp.myIsAdjust);
     std::swap(myNeedArrayUpdate,   Temp.myNeedArrayUpdate);
     std::swap(myNeedTextureUpdate, Temp.myNeedTextureUpdate);
     myPixels.swap(Temp.myPixels);
@@ -729,6 +751,7 @@
     myTextureHeight     = 0;
     myTexture           = 0;
     myIsSmooth          = true;
+    myIsAdjust          = true;
     myNeedTextureUpdate = false;
     myNeedArrayUpdate   = false;
     myPixels.clear();
Title: subpixel accuracy of sprite of image
Post by: Wizzard on November 27, 2008, 01:01:34 am
This is a nice patch, thanks! I'd recommend that you make it static though.
Title: subpixel accuracy of sprite of image
Post by: Laurent on November 27, 2008, 08:13:03 am
Well, this half-texel adjustment is not really a feature that should be available as an option in the public interface, it's a "bug" fix actually. Without it, pixels are (sometimes) not rendered exactly as they should, this is due to the way texels are mapped by OpenGL. I thought the current code was able to make it look ok in all situations, but apparently not ;)

Can you show me your examples? This will help me understand when this fix is necessary, and when it should not be applied.

Thanks for your feedback :)
Title: subpixel accuracy of sprite of image
Post by: T.T.H. on November 27, 2008, 06:58:43 pm
This (http://www.teeteehaa.de/SFML/TestSFMLSeamless.zip) is my proof-of-concept example (1.8 MB zipped VC++2005 Express project plus some images). You need to have my above mentioned patch applied to SFML to make it work.

Type 's' to toggle texture smoothing and type 'a' to toggle half-texel adjusment (note: half-texel adjustment is only meaningful in combination with texture smooting turned on).