Results 1 to 5 of 5

Thread: SDL, sloped pixel lines

  1. #1
    Member
    Join Date
    Apr 2014
    Location
    Lower Saxony, Germany
    Posts
    38

    SDL, sloped pixel lines

    SDL provides 2D accelerated rendering including pixel points, rectangles and lines. Furthermore you can set device independent resolution and nearest pixel sampling. So you can use the resolution of the C64 (320 x 200) on a big window (1280 x 800) for example. The pixels are scaled accordingly.

    But not always. If you draw a line then it's pixels are only scaled if that line ist either a horizontal or a vertical one. Sloped lines are rendered without pixel scaling.
    That's because lines are rendered as rectangles internally:

    Code:
    static int
    RenderDrawLinesWithRects(SDL_Renderer * renderer,
                         const SDL_Point * points, const int count)
    {
        SDL_FRect *frect;
        SDL_FRect *frects;
        SDL_FPoint fpoints[2];
        int i, nrects = 0;
        int retval = 0;
        SDL_bool isstack;
    
        frects = SDL_small_alloc(SDL_FRect, count-1, &isstack);
        if (!frects) {
            return SDL_OutOfMemory();
        }
    
        for (i = 0; i < count-1; ++i) {
            if (points[i].x == points[i+1].x) {
                const int minY = SDL_min(points[i].y, points[i+1].y);
                const int maxY = SDL_max(points[i].y, points[i+1].y);
    
                frect = &frects[nrects++];
                frect->x = points[i].x * renderer->scale.x;
                frect->y = minY * renderer->scale.y;
                frect->w = renderer->scale.x;
                frect->h = (maxY - minY + 1) * renderer->scale.y;
            } else if (points[i].y == points[i+1].y) {
                const int minX = SDL_min(points[i].x, points[i+1].x);
                const int maxX = SDL_max(points[i].x, points[i+1].x);
    
                frect = &frects[nrects++];
                frect->x = minX * renderer->scale.x;
                frect->y = points[i].y * renderer->scale.y;
                frect->w = (maxX - minX + 1) * renderer->scale.x;
                frect->h = renderer->scale.y;
            } else {
                /* FIXME: We can't use a rect for this line... */
                fpoints[0].x = points[i].x * renderer->scale.x;
                fpoints[0].y = points[i].y * renderer->scale.y;
                fpoints[1].x = points[i+1].x * renderer->scale.x;
                fpoints[1].y = points[i+1].y * renderer->scale.y;
                retval += QueueCmdDrawLines(renderer, fpoints, 2);
            }
        }
    
        retval += QueueCmdFillRects(renderer, frects, nrects);
    
        SDL_small_free(frects, isstack);
    
        if (retval < 0) {
            retval = -1;
        }
        return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
    }
    Did you notice that FIXME? -- I wrote an additional procedure in order to draw sloped pixel lines. It makes use of that good old Bresenham algorithm. Also each line is devided into horizontal or vertical segments which can be rendered quite fast by SDL:

    Code:
    procedure SDL_RenderDrawLineSloped(renderer: PSDL_renderer; x1, y1, x2, y2: longint);
    var
      dx, dy, rx, ry: longint;
      error, c: longint;
    begin
    
      dy := y2-y1;
      if dy>=0 then ry := 1
      else begin
        dy := -dy;
        ry := -1
      end;
      y2 := y1;
    
      dx := x2-x1;
      if dx>=0 then rx := 1
      else begin
        dx := -dx;
        rx := -1
      end;
      x2 := x1;
    
      { slope is moderate (dy/dx <= 1) }
      if dx>=dy then begin
    
        error := dx shr 1;
        for c := 0 to dx do begin
          inc(x2, rx);
          dec(error, dy);
          if error<0 then begin
    
            SDL_RenderDrawLine(renderer, x1, y1, x2-rx, y1);
    
            x1 := x2;
            inc(error, dx);
            inc(y1, ry);
          end;
        end;
    
        { slope is very moderate (dy/dx <= 0.5) : complete line ending }
        if dx>=dy+dy then SDL_RenderDrawLine(renderer, x1, y1, x2-rx, y1);
    
      end
      { slope is steep (dy/dx > 1) }
      else begin
    
        error := dy shr 1;
        for c := 0 to dy do begin
          inc(y2, ry);
          dec(error, dx);
          if error<0 then begin
    
            SDL_RenderDrawLine(renderer, x1, y1, x1, y2-ry);
    
            y1 := y2;
            inc(error, dy);
            inc(x1, rx);
          end;
        end;
    
        { slope is very steep (dy/dx >= 2) : complete line ending }
        if dy>=dx+dx then SDL_RenderDrawLine(renderer, x1, y1, x1, y2-ry);
    
      end;
    end;

  2. #2
    Quote Originally Posted by klausvdl View Post
    But not always. If you draw a line then it's pixels are only scaled if that line ist either a horizontal or a vertical one. Sloped lines are rendered without pixel scaling.
    That's because lines are rendered as rectangles internally:
    Would you mind explaining of what you mean by "Sloped lines are rendered without pixel scaling"?
    Perhaps by posting two example images. One showing sloped line with pixel scaling and one without.

  3. #3
    Member
    Join Date
    Apr 2014
    Location
    Lower Saxony, Germany
    Posts
    38
    Quote Originally Posted by SilverWarior View Post
    Would you mind explaining of what you mean by "Sloped lines are rendered without pixel scaling"?
    Perhaps by posting two example images. One showing sloped line with pixel scaling and one without.
    This is a screenshot from a window with the physical size of 960 x 540.
    The logical size was set to 320 x 180, so each rendered pixel has internally to be scaled up (3 x).
    But the thin lines are rendered by SDL's own line renderer and unfortunately the pixels are not scaled up.
    The thick lines are rendered with the new procedure.

    line demo.jpg

  4. #4
    OK! Now I see what are you talking about. Couldn't really figure that out because me and C-like syntax don't get along pretty well

    Any way looking at your code I'm wondering a few things.
    1. When calculating dx and dy values why don't you simply use:
      Code:
      dx := ABS(x2-x1);
      dy := ABS(y2-y1);
    2. Also looking at you code it seems as if you are using a for loop to determine the width of line rectangles. Isn't that very slow?
      Wouldn't it be easier to calculate the width of horizontal lines (line segments) that will represent your diagonal line by simply dividing the dx with dy?

  5. #5
    Member
    Join Date
    Apr 2014
    Location
    Lower Saxony, Germany
    Posts
    38
    Quote Originally Posted by SilverWarior View Post
    Also looking at you code it seems as if you are using a for loop to determine the width of line rectangles. Isn't that very slow?
    Wouldn't it be easier to calculate the width of horizontal lines (line segments) that will represent your diagonal line by simply dividing the dx with dy?
    You're right ...

    I adapted a line algorithm intended for drawing single pixels. That code has to iterate over every pixel of the line. But that's unnecessary when drawing horizontal or vertical line segments instead of pixels.
    So I followed your hint and rewrote the code:

    Code:
    procedure draw_line(renderer: psdl_renderer; x1, y1, x2, y2: longint);
    var
      dx, dy, rx, ry: longint;
      error, c: longint;
      step, rest: longint;
    begin
    
      { if the whole line is horizontal or vertical than use SDL_RenderDrawLine and exit
        if not, we know that neither dx nor dy can be zero (division by zero not possible) }
      if (y1=y2) or (x1=x2) then begin
        sdl_renderdrawline(renderer, x1, y1, x2, y2);
        exit;
      end;
    
      dy := y2-y1;
      if dy>=0 then ry := 1
      else begin
        dy := -dy;
        ry := -1
      end;
      inc(dy);
    
      dx := x2-x1;
      if dx>=0 then rx := 1
      else begin
        dx := -dx;
        rx := -1
      end;
      inc(dx);
    
      { slope is moderate (dy/dx <= 1) }
      if dx>=dy then begin
    
        step := dx div dy;
        rest := dx-(dy*step);
        step := step*rx;
    
        x2 := x1;
    
        error := dy shr 1;
        for c := 1 to dy do begin
    
          dec(error, rest);
          if error<0 then begin
            inc(error, dy);
            inc(x2, rx);
          end;
    
          inc(x2, step);
          sdl_renderdrawline(renderer, x1, y1, x2-rx, y1);
          x1 := x2;
    
          inc(y1, ry);
        end;
    
      end
      { slope is steep (dy/dx > 1) }
      else begin
    
        step := dy div dx;
        rest := dy-(dx*step);
        step := step*ry;
    
        y2 := y1;
    
        error := dx shr 1;
        for c := 1 to dx do begin
    
          dec(error, rest);
          if error<0 then begin
            inc(error, dx);
            inc(y2, ry);
          end;
    
          inc(y2, step);
          sdl_renderdrawline(renderer, x1, y1, x1, y2-ry);
          y1 := y2;
    
          inc(x1, rx);
        end;
    
      end;
    end;
    Regarding your other question: I have to know the signs of the deltas (dx, dy) and store them in the rates (rx, ry) for later use. After that I make the deltas positive.

    Thanks for following my trial-and-error technique.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •