This file is indexed.

/usr/share/pyshared/kivy/atlas.py is in python-kivy 1.7.2-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
'''
Atlas
=====

.. versionadded:: 1.1.0

Atlas is a class for managing textures atlases: packing multiple texture into
one. With it, you are reducing the number of image to load and speedup the
application loading.

An Atlas is composed of:

    - a json file (.atlas) that contain all the information about the image
      contained inside the atlas.
    - one or multiple atlas image associated to the atlas definition.

Definition of .atlas
--------------------

A file with ``<basename>.atlas`` is a json file formatted like this::

    {
        "<basename>-<index>.png": {
            "id1": [ <x>, <y>, <width>, <height> ],
            "id2": [ <x>, <y>, <width>, <height> ],
            # ...
        },
        # ...
    }

Example of the Kivy ``defaulttheme.atlas``::

    {
        "defaulttheme-0.png": {
            "progressbar_background": [431, 224, 59, 24],
            "image-missing": [253, 344, 48, 48],
            "filechooser_selected": [1, 207, 118, 118],
            "bubble_btn": [83, 174, 32, 32],
            # ... and more ...
        }
    }

How to create an atlas
----------------------

.. warning::

    The atlas creation require Imaging/PIL. This will be removed in the future
    when Kivy core Image will be able to support loading / blitting / save
    operation.

You can directly use this module to create atlas file with this command::

    $ python -m kivy.atlas <basename> <size> <list of images...>

Let's say you have a list of image that you want to put into an Atlas. The
directory is named ``images`` with lot of png::

    $ ls
    images
    $ cd images
    $ ls
    bubble.png bubble-red.png button.png button-down.png

You can combine all the png into one, and generate the atlas file with::

    $ python -m kivy.atlas myatlas 256 *.png
    Atlas created at myatlas.atlas
    1 image have been created
    $ ls
    bubble.png bubble-red.png button.png button-down.png myatlas.atlas
    myatlas-0.png

As you can see, we got 2 new files: ``myatlas.atlas`` and ``myatlas-0.png``.

.. note::

    When using this script, the ids referenced in the atlas is the basename of
    the image, without the extension. So if you are going to give a filename
    ``../images/button.png``, the id for this image will be ``button``.

How to use an atlas
-------------------

Usually, you are doing something like this::

    a = Button(background_normal='images/button.png',
               background_down='images/button_down.png')

In our previous example, we have created the atlas containing both of them, and
put it in ``images/myatlas.atlas``. You can use the url notation to reference
them::

    atlas://path/to/myatlas/id
    # will search for the ``path/to/myatlas.atlas``, and get the image ``id``

In our case, it will be::

    atlas://images/myatlas/button

.. note::

    In the atlas url, their is no need to put the ``.atlas`` extension, it will
    be automatically append to the filename.

Manual usage of the Atlas
-------------------------

::

    >>> from kivy.atlas import Atlas
    >>> atlas = Atlas('path/to/myatlas.atlas')
    >>> print atlas.textures.keys()
    ['bubble', 'bubble-red', 'button', 'button-down']
    >>> print atlas['button']
    <kivy.graphics.texture.TextureRegion object at 0x2404d10>
'''

__all__ = ('Atlas', )

import json
from os.path import basename, dirname, join, splitext
from kivy.event import EventDispatcher
from kivy.logger import Logger
from kivy.properties import AliasProperty, DictProperty
import os


# late import to prevent recursion
CoreImage = None


class Atlas(EventDispatcher):
    '''Manage texture atlas. See module documentation for more information.
    '''

    textures = DictProperty({})
    '''List of available textures within the atlas.

    :data:`textures` is a :class:`~kivy.properties.DictProperty`, default to {}
    '''

    def _get_filename(self):
        return self._filename

    filename = AliasProperty(_get_filename, None)
    '''Filename of the current Atlas

    :data:`filename` is a :class:`~kivy.properties.AliasProperty`, default to
    None
    '''

    def __init__(self, filename):
        self._filename = filename
        super(Atlas, self).__init__()
        self._load()

    def __getitem__(self, key):
        return self.textures[key]

    def _load(self):
        # late import to prevent recursive import.
        global CoreImage
        if CoreImage is None:
            from kivy.core.image import Image as CoreImage

        # must be a name finished by .atlas ?
        filename = self._filename
        assert(filename.endswith('.atlas'))
        filename = filename.replace('/', os.sep)

        Logger.debug('Atlas: Load <%s>' % filename)
        with open(filename, 'r') as fd:
            meta = json.load(fd)

        Logger.debug('Atlas: Need to load %d images' % len(meta))
        d = dirname(filename)
        textures = {}
        for subfilename, ids in meta.iteritems():
            subfilename = join(d, subfilename)
            Logger.debug('Atlas: Load <%s>' % subfilename)

            # load the image
            ci = CoreImage(subfilename)

            # for all the uid, load the image, get the region, and put it in our
            # dict.
            for meta_id, meta_coords in ids.iteritems():
                x, y, w, h = meta_coords
                textures[meta_id] = ci.texture.get_region(*meta_coords)

        self.textures = textures

    @staticmethod
    def create(outname, filenames, size, padding=2):
        '''This method can be used to create manually an atlas from a set of
        images.

        :Parameters:
            `outname`: str
                Basename to use for ``.atlas`` creation and ``-<idx>.png``
                associated images.
            `filenames`: list
                List of filename to put in the atlas
            `size`: int
                Size of an atlas image
            `padding`: int, default to 2
                Padding to put around each image.

                Be careful. If you're using a padding < 2, you might get issues
                with border of the images. Because of the OpenGL linearization,
                it might take the pixels of the adjacent image.

                If you're using a padding >= 2, we'll automatically generate a
                "border" of 1px of your image, around the image. If you look at
                the result, don't be scared if the image inside it are not
                exactly the same as yours :).
        '''
        # Thanks to
        # omnisaurusgames.com/2011/06/texture-atlas-generation-using-python/
        # for its initial implementation.
        try:
            from PIL import Image
        except ImportError:
            Logger.critical('Atlas: Imaging/PIL are missing')
            raise

        size = int(size)

        # open all of the images
        ims = [(f, Image.open(f)) for f in filenames]

        # sort by image area
        ims = sorted(ims, key=lambda im: im[1].size[0] * im[1].size[1],
                reverse=True)

        # free boxes are empty space in our output image set
        # the freebox tuple format is: outidx, x, y, w, h
        freeboxes = [(0, 0, 0, size, size)]
        numoutimages = 1

        # full boxes are areas where we have placed images in the atlas
        # the full box tuple format is: image, outidx, x, y, w, h, filename
        fullboxes = []

        # do the actual atlasing by sticking the largest images we can have into
        # the smallest valid free boxes
        for imageinfo in ims:
            im = imageinfo[1]
            imw, imh = im.size
            imw += padding
            imh += padding
            if imw > size or imh > size:
                Logger.error('Atlas: image %s is larger than the atlas size!' %
                    imageinfo[0])
                return

            inserted = False
            while not inserted:
                for idx, fb in enumerate(freeboxes):
                    # find the smallest free box that will contain this image
                    if fb[3] >= imw and fb[4] >= imh:
                        # we found a valid spot! Remove the current freebox, and
                        # split the leftover space into (up to) two new
                        # freeboxes
                        del freeboxes[idx]
                        if fb[3] > imw:
                            freeboxes.append((
                                fb[0], fb[1] + imw, fb[2],
                                fb[3] - imw, imh))

                        if fb[4] > imh:
                            freeboxes.append((
                                fb[0], fb[1], fb[2] + imh,
                                fb[3], fb[4] - imh))

                        # keep this sorted!
                        freeboxes = sorted(freeboxes,
                                key=lambda fb: fb[3] * fb[4])
                        fullboxes.append((im,
                            fb[0], fb[1] + padding,
                            fb[2] + padding, imw - padding,
                            imh - padding, imageinfo[0]))
                        inserted = True
                        break

                if not inserted:
                    # oh crap - there isn't room in any of our free boxes, so we
                    # have to add a new output image
                    freeboxes.append((numoutimages, 0, 0, size, size))
                    numoutimages += 1

        # now that we've figured out where everything goes, make the output
        # images and blit the source images to the approriate locations
        Logger.info('Atlas: create an {0}x{0} rgba image'.format(size))
        outimages = [Image.new('RGBA', (size, size))
                for i in range(0, int(numoutimages))]
        for fb in fullboxes:
            x, y = fb[2], fb[3]
            out = outimages[fb[1]]
            out.paste(fb[0], (fb[2], fb[3]))
            w, h = fb[0].size
            if padding > 1:
                out.paste(fb[0].crop((0, 0, w, 1)), (x, y - 1))
                out.paste(fb[0].crop((0, h - 1, w, h)), (x, y + h))
                out.paste(fb[0].crop((0, 0, 1, h)), (x - 1, y))
                out.paste(fb[0].crop((w - 1, 0, w, h)), (x + w, y))

        # save the output images
        for idx, outimage in enumerate(outimages):
            outimage.save('%s-%d.png' % (outname, idx))

        # write out an json file that says where everything ended up
        meta = {}
        for fb in fullboxes:
            fn = '%s-%d.png' % (basename(outname), fb[1])
            if fn not in meta:
                d = meta[fn] = {}
            else:
                d = meta[fn]

            # fb[6] contain the filename aka '../apok.png'. just get only 'apok'
            # as the uniq id.
            uid = splitext(basename(fb[6]))[0]
            x, y, w, h = fb[2:6]
            d[uid] = x, size - y - h, w, h

        outfn = '%s.atlas' % outname
        with open(outfn, 'w') as fd:
            json.dump(meta, fd)

        return outfn, meta

if __name__ == '__main__':

    import sys
    argv = sys.argv[1:]
    if len(argv) < 3:
        print 'Usage: python -m kivy.atlas <outname> <size> <img1.png>' \
              '[<img2.png>, ...]'
        sys.exit(1)

    outname = argv[0]
    try:
        size = int(argv[1])
    except ValueError:
        print 'Error: size must be an integer'
        sys.exit(1)

    filenames = argv[2:]
    ret = Atlas.create(outname, filenames, size)
    if not ret:
        print 'Error while creating atlas!'
        sys.exit(1)

    fn, meta = ret
    print 'Atlas created at', fn
    print '%d image%s have been created' % (len(meta),
            's' if len(meta) > 1 else '')