I need a fast way to create a list of tuples representing image pixel coordinates *(**X**, **Y**)*.

Where **X** is from `0` to `size` and **Y** is from `0` to `size`.

A step value of `1` results in **X** and **Y** values of (0, 1, 2, 3...) which is too many tuples. Using a step value greater than `1` will reduce processing time. For example, if the step value is `2` the values would be (0, 2, 4, 6...). If the step value is `4` the values would be (0, 4, 8, 12...).

In pure python `range` command might be used. However, `NumPy` is installed by default in my Linux distribution. In NumPy the `arrange` command might be used but I'm having a hard time wrapping my mind around NumPy array syntax.

PS: After a list of tuples is created it will be randomly shuffled and then read in the loop.

---

Edit 1
------

Using [this answer](https://stackoverflow.com/a/65971039/6929343) below:

[![fade in answer 1.gif](https://i.stack.imgur.com/0X7vo.gif)](https://i.stack.imgur.com/0X7vo.gif)

Instead of the image fading in it's doing some kind of weird wipe left to right. Using the code from the answer with a slight modification:

```
        step = 4
        size = self.play_rotated_art.size[0] - step

        self.xy_list = [
            (x, y)
            for x in range(0, size - step, step)
            for y in range(0, size - step, step)
        ]

```

### Bug Update

There was an error in my code, it's working fine now:

[![fade in answer 1 fix.gif](https://i.stack.imgur.com/rNy8j.gif)](https://i.stack.imgur.com/rNy8j.gif)

The updated code is:

```
        self.step = 4
        size = self.play_rotated_art.size[0] - self.step

        self.xy_list = [
            (x, y)
            for x in range(0, size - self.step, self.step)
            for y in range(0, size - self.step, self.step)
        ]

        shuffle(self.xy_list)
        # Convert numpy array into python list & calculate chunk size
        self.current_chunk = 0
        self.chunk_size = int(len(self.xy_list) / 100)

    # Where we stop copying pixels for current 1% chunck
    end = self.current_chunk + self.chunk_size
    if end > len(self.xy_list) - 1:
       end = len(self.xy_list) - 1

    while self.current_chunk < end:
        x0, y0 = self.xy_list[self.current_chunk]
        x1 = x0 + self.step
        y1 = y0 + self.step
        box = (x0, y0, x1, y1)
        region = self.play_rotated_art.crop(box)
        self.fade.paste(region, box)
        self.current_chunk += 1

    self.play_artfade_count += 1
    return self.fade

```

---

TL;DR
=====

I already have code with step value `1` but this code is overly complex and inefficient to request a modification. The above generic question would help others more and, still help me, if it were answered.

Existing code with step value `1`:
----------------------------------

```
def play_artfade2(self):
    ''' PILLOW VERSION:
        Fade in artwork in 100 chunks leaving loop after chunk and
        reentering after Tkinter updates screen and pauses.
    '''
    if self.play_artfade_count == 100:
        # We'have completed a full cycle. Force graphical effects exit
        self.play_artfade_count = 0         # Reset art fade count
        self.play_rotated_value = -361      # Force Spin Art
        return None

    # Initialize numpy arrays first time through
    if self.play_artfade_count == 0:

        # Create black image to fade into
        self.fade = Image.new('RGBA', self.play_rotated_art.size, \
                              color='black')

        # Generate a randomly shuffled array of the coordinates
        im = np.array(self.play_rotated_art)
        X,Y = np.where(im[...,0]>=0)
        coords = np.column_stack((X,Y))
        np.random.shuffle(coords)

        # Convert numpy array into python list & calculate chunk size
        self.xy_list = list(coords)
        self.current_chunk = 0
        self.chunk_size = int(len(self.xy_list) / 100)

    # Where we stop copying pixels for current 1% chunck
    end = self.current_chunk + self.chunk_size
    if end > len(self.xy_list) - 1:
       end = len(self.xy_list) - 1

    while self.current_chunk < end:
        x0, y0 = self.xy_list[self.current_chunk]
        x1 = x0 + 1
        y1 = y0 + 1
        box = (x0, y0, x1, y1)
        region = self.play_rotated_art.crop(box)
        self.fade.paste(region, box)
        self.current_chunk += 1

    self.play_artfade_count += 1
    return self.fade

```

Using Pillow's `Image.crop()` and `Image.paste()` is overkill for a single pixel but the initial ***working*** design was future focused to utilize "super pixels" with box size of 2x2, 3x3, 5x5, etc as image is resized from 200x200 to 333x333 to 512x512, etc.