<commit_before>/*
    Copyright (C) 1998 by Jorrit Tyberghein

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "sysdef.h"
#include "qint.h"
#include "csutil/garray.h"
#include "csutil/util.h"
#include "tcache.h"
#include "soft_g3d.h"
#include "soft_txt.h"
#include "ilghtmap.h"
#include "isystem.h"

static int hash_table [384];

//------------------------------------------------------------------------------

#define LM_NAME		csTextureCacheSoftware::create_lighted_texture_8
#define PI_INDEX8
#include "lightmap.inc"

#define LM_NAME		csTextureCacheSoftware::create_lighted_texture_555
#define PI_R5G5B5
#include "lightmap.inc"

#define LM_NAME		csTextureCacheSoftware::create_lighted_texture_565
#define PI_R5G6B5
#include "lightmap.inc"

#define LM_NAME		csTextureCacheSoftware::create_lighted_texture_888
#define PI_R8G8B8
#include "lightmap.inc"

//------------------------------------------------- csTextureCacheSoftware ---//

//  static void (*create_lighted_texture) (iPolygonTexture *pt,
//    SoftwareCachedTexture *ct, csTextureManagerSoftware *texman,
//    float u_min, float v_min, float u_max, float v_max);

static void compute_hash_table ()
{
  for (int i = 0; i < 384; i++)
    hash_table [i] = i + 64;
}

csTextureCacheSoftware::csTextureCacheSoftware (csTextureManagerSoftware *TexMan)
{
  head = tail = NULL;
  texman = TexMan;
  frameno = 0;
  Clear ();
  bytes_per_texel = texman->pfmt.PixelBytes;
  if (texman->pfmt.PixelBytes == 1)
    create_lighted_texture = &csTextureCacheSoftware::create_lighted_texture_8;
  else if (texman->pfmt.PixelBytes == 2)
    if (texman->pfmt.GreenBits == 5)
      create_lighted_texture = &csTextureCacheSoftware::create_lighted_texture_555;
    else
      create_lighted_texture = &csTextureCacheSoftware::create_lighted_texture_565;
  else if (texman->pfmt.PixelBytes == 4)
    create_lighted_texture = &csTextureCacheSoftware::create_lighted_texture_888;
  else
    abort (); // huh???
  compute_hash_table ();
}

csTextureCacheSoftware::~csTextureCacheSoftware ()
{
  Clear ();
}

void csTextureCacheSoftware::set_cache_size (long size)
{
  Clear ();
  cache_size = size;
}

void csTextureCacheSoftware::Clear ()
{
  while (head)
  {
    SoftwareCachedTexture *n = head->next;
    delete head;
    head = n;
  }

  head = tail = NULL;
  total_size = 0;
  total_textures = 0;
}

void csTextureCacheSoftware::uncache_texture (int MipMap, iPolygonTexture* pt)
{
  SoftwareCachedTexture* cached_texture;
  cached_texture = (SoftwareCachedTexture*)pt->GetCacheData (MipMap);
  if (!cached_texture) return;

  if (cached_texture->next)
    cached_texture->next->prev = cached_texture->prev;
  else
    tail = cached_texture->prev;
  if (cached_texture->prev)
    cached_texture->prev->next = cached_texture->next;
  else
    head = cached_texture->next;
  total_textures--;
  total_size -= cached_texture->size;

  delete cached_texture;
}

SoftwareCachedTexture *csTextureCacheSoftware::cache_texture (
  int MipMap, iPolygonTexture* pt)
{
  SoftwareCachedTexture *cached_texture =
    (SoftwareCachedTexture *)pt->GetCacheData (MipMap);

  if (cached_texture)
  {
    // Texture is already in the cache.
    // Unlink texture and put it in front (MRU).
    if (cached_texture != head)
    {
      if (cached_texture->prev)
        cached_texture->prev->next = cached_texture->next;
      else
        head = cached_texture->next;
      if (cached_texture->next)
        cached_texture->next->prev = cached_texture->prev;
      else
        tail = cached_texture->prev;

      cached_texture->prev = NULL;
      cached_texture->next = head;
      if (head)
        head->prev = cached_texture;
      else
        tail = cached_texture;
      head = cached_texture;
    }
    cached_texture->frameno = frameno;
  }
  else
  {
    // Texture is not in the cache.
    int lightmap_size = pt->GetLightMap ()->GetSize () * sizeof (ULong);
    int bitmap_w = (pt->GetWidth () >> MipMap);
    int bitmap_h = ((pt->GetHeight () + (1 << MipMap) - 1) >> MipMap);
    int bitmap_size = lightmap_size + bytes_per_texel * bitmap_w * (H_MARGIN * 2 + bitmap_h);

    total_textures++;
    total_size += bitmap_size;

    // Free lightmaps until we have less than cache_size bytes for the cache

    while (tail && (total_size > cache_size))
    {
      // Total size of textures in cache is too high. Remove the last one.
      cached_texture = tail;

      // thanks John Carmack: we miss you :-)
      // A possible improvement for the texture cache algorithm could be the
      // following: the problem with it currently is that it always frees the
      // least-recently-used texture, no matter when it was allocated. For
      // levels where all the textures visible in one frame do not fit in the
      // cache this is extremely ugly: the textures are computed all in chain
      // again and again because each new textures pushes the least recently
      // used (but also generated during this frame) texture out of cache and
      // so on. This could be improved by adding a "frame number" to each
      // texture, and to check if LRU texture has same frame number as the
      // current one; if so, we should free the MOSTLY allocated texture
      // instead. This way, we'll compute just several textures on each
      // frame instead of doing it for ALL of them.
      if (cached_texture->frameno == frameno)
#if 1
        // Hmm... this proven to work much better ... WHY??? :-)
        cached_texture = head;
#else
      {
        // Try to find the closest (by size) texture cached this frame
        int need_size = total_size - cache_size;
        int nearest_size = tail->size;
        for (SoftwareCachedTexture *cur = head; cur != tail; cur = cur->next)
          if (((cur->size >= need_size)
            && (cur->size < nearest_size))
           || ((nearest_size < need_size)
            && (nearest_size < cur->size)))
          {
            cached_texture = cur;
            nearest_size = cur->size;
          }
      }
#endif

      if (cached_texture->prev)
        cached_texture->prev->next = cached_texture->next;
      else
        head = cached_texture->next;
      if (cached_texture->next)
        cached_texture->next->prev = cached_texture->prev;
      else
        tail = cached_texture->prev;

      total_textures--;
      total_size -= cached_texture->size;

      delete cached_texture;
    }

    CHK (cached_texture = new SoftwareCachedTexture (MipMap, pt));
    cached_texture->frameno = frameno;

    int margin_size = H_MARGIN * bitmap_w * bytes_per_texel;
    UByte *data = new UByte [bitmap_size];
    memset (data, 0, lightmap_size);
    cached_texture->data = data;
    cached_texture->bitmap = data + lightmap_size + margin_size; // Skip margin.
    cached_texture->size = bitmap_size;

    // Add new texture to cache.
    cached_texture->next = head;
    cached_texture->prev = NULL;
    if (head)
      head->prev = cached_texture;
    else
      tail = cached_texture;
    head = cached_texture;
  }
  return cached_texture;
}

void csTextureCacheSoftware::fill_texture (int MipMap, iPolygonTexture* pt,
  float u_min, float v_min, float u_max, float v_max)
{
  // Recalculate the lightmaps
  pt->RecalculateDynamicLights ();

  // Now cache the texture
  SoftwareCachedTexture *cached_texture = cache_texture (MipMap, pt);

  // Compute the rectangle on the lighted texture, if it is dirty
  create_lighted_texture (pt, cached_texture, texman, u_min, v_min, u_max, v_max);
}

#define SysPrintf iG3D->System->Printf

void csTextureCacheSoftware::dump (csGraphics3DSoftwareCommon *iG3D)
{
  SysPrintf (MSG_CONSOLE, "Textures in the cache: %d\n", total_textures);
  SysPrintf (MSG_CONSOLE, "Total size: %ld bytes\n", total_size);
  int mean = (total_textures == 0) ? 0 : total_size / total_textures;
  SysPrintf (MSG_CONSOLE, "Bytes per texture: %d\n", mean);
}
<commit_msg>Now compiles with NextStep compiler.<commit_after>/*
    Copyright (C) 1998 by Jorrit Tyberghein

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "sysdef.h"
#include "qint.h"
#include "csutil/garray.h"
#include "csutil/util.h"
#include "tcache.h"
#include "soft_g3d.h"
#include "soft_txt.h"
#include "ilghtmap.h"
#include "isystem.h"

static int hash_table [384];

//------------------------------------------------------------------------------

#define LM_NAME		csTextureCacheSoftware::create_lighted_texture_8
#define PI_INDEX8
#include "lightmap.inc"

#define LM_NAME		csTextureCacheSoftware::create_lighted_texture_555
#define PI_R5G5B5
#include "lightmap.inc"

#define LM_NAME		csTextureCacheSoftware::create_lighted_texture_565
#define PI_R5G6B5
#include "lightmap.inc"

#define LM_NAME		csTextureCacheSoftware::create_lighted_texture_888
#define PI_R8G8B8
#include "lightmap.inc"

//------------------------------------------------- csTextureCacheSoftware ---//

//  static void (*create_lighted_texture) (iPolygonTexture *pt,
//    SoftwareCachedTexture *ct, csTextureManagerSoftware *texman,
//    float u_min, float v_min, float u_max, float v_max);

static void compute_hash_table ()
{
  for (int i = 0; i < 384; i++)
    hash_table [i] = i + 64;
}

csTextureCacheSoftware::csTextureCacheSoftware (csTextureManagerSoftware *TexMan)
{
  head = tail = NULL;
  texman = TexMan;
  frameno = 0;
  Clear ();
  bytes_per_texel = texman->pfmt.PixelBytes;
  if (texman->pfmt.PixelBytes == 1)
    create_lighted_texture = &csTextureCacheSoftware::create_lighted_texture_8;
  else if (texman->pfmt.PixelBytes == 2)
    if (texman->pfmt.GreenBits == 5)
      create_lighted_texture = &csTextureCacheSoftware::create_lighted_texture_555;
    else
      create_lighted_texture = &csTextureCacheSoftware::create_lighted_texture_565;
  else if (texman->pfmt.PixelBytes == 4)
    create_lighted_texture = &csTextureCacheSoftware::create_lighted_texture_888;
  else
    abort (); // huh???
  compute_hash_table ();
}

csTextureCacheSoftware::~csTextureCacheSoftware ()
{
  Clear ();
}

void csTextureCacheSoftware::set_cache_size (long size)
{
  Clear ();
  cache_size = size;
}

void csTextureCacheSoftware::Clear ()
{
  while (head)
  {
    SoftwareCachedTexture *n = head->next;
    delete head;
    head = n;
  }

  head = tail = NULL;
  total_size = 0;
  total_textures = 0;
}

void csTextureCacheSoftware::uncache_texture (int MipMap, iPolygonTexture* pt)
{
  SoftwareCachedTexture* cached_texture;
  cached_texture = (SoftwareCachedTexture*)pt->GetCacheData (MipMap);
  if (!cached_texture) return;

  if (cached_texture->next)
    cached_texture->next->prev = cached_texture->prev;
  else
    tail = cached_texture->prev;
  if (cached_texture->prev)
    cached_texture->prev->next = cached_texture->next;
  else
    head = cached_texture->next;
  total_textures--;
  total_size -= cached_texture->size;

  delete cached_texture;
}

SoftwareCachedTexture *csTextureCacheSoftware::cache_texture (
  int MipMap, iPolygonTexture* pt)
{
  SoftwareCachedTexture *cached_texture =
    (SoftwareCachedTexture *)pt->GetCacheData (MipMap);

  if (cached_texture)
  {
    // Texture is already in the cache.
    // Unlink texture and put it in front (MRU).
    if (cached_texture != head)
    {
      if (cached_texture->prev)
        cached_texture->prev->next = cached_texture->next;
      else
        head = cached_texture->next;
      if (cached_texture->next)
        cached_texture->next->prev = cached_texture->prev;
      else
        tail = cached_texture->prev;

      cached_texture->prev = NULL;
      cached_texture->next = head;
      if (head)
        head->prev = cached_texture;
      else
        tail = cached_texture;
      head = cached_texture;
    }
    cached_texture->frameno = frameno;
  }
  else
  {
    // Texture is not in the cache.
    int lightmap_size = pt->GetLightMap ()->GetSize () * sizeof (ULong);
    int bitmap_w = (pt->GetWidth () >> MipMap);
    int bitmap_h = ((pt->GetHeight () + (1 << MipMap) - 1) >> MipMap);
    int bitmap_size = lightmap_size + bytes_per_texel * bitmap_w * (H_MARGIN * 2 + bitmap_h);

    total_textures++;
    total_size += bitmap_size;

    // Free lightmaps until we have less than cache_size bytes for the cache

    while (tail && (total_size > cache_size))
    {
      // Total size of textures in cache is too high. Remove the last one.
      cached_texture = tail;

      // thanks John Carmack: we miss you :-)
      // A possible improvement for the texture cache algorithm could be the
      // following: the problem with it currently is that it always frees the
      // least-recently-used texture, no matter when it was allocated. For
      // levels where all the textures visible in one frame do not fit in the
      // cache this is extremely ugly: the textures are computed all in chain
      // again and again because each new textures pushes the least recently
      // used (but also generated during this frame) texture out of cache and
      // so on. This could be improved by adding a "frame number" to each
      // texture, and to check if LRU texture has same frame number as the
      // current one; if so, we should free the MOSTLY allocated texture
      // instead. This way, we'll compute just several textures on each
      // frame instead of doing it for ALL of them.
      if (cached_texture->frameno == frameno)
#if 1
        // Hmm... this proven to work much better ... WHY??? :-)
        cached_texture = head;
#else
      {
        // Try to find the closest (by size) texture cached this frame
        int need_size = total_size - cache_size;
        int nearest_size = tail->size;
        for (SoftwareCachedTexture *cur = head; cur != tail; cur = cur->next)
          if (((cur->size >= need_size)
            && (cur->size < nearest_size))
           || ((nearest_size < need_size)
            && (nearest_size < cur->size)))
          {
            cached_texture = cur;
            nearest_size = cur->size;
          }
      }
#endif

      if (cached_texture->prev)
        cached_texture->prev->next = cached_texture->next;
      else
        head = cached_texture->next;
      if (cached_texture->next)
        cached_texture->next->prev = cached_texture->prev;
      else
        tail = cached_texture->prev;

      total_textures--;
      total_size -= cached_texture->size;

      delete cached_texture;
    }

    CHK (cached_texture = new SoftwareCachedTexture (MipMap, pt));
    cached_texture->frameno = frameno;

    int margin_size = H_MARGIN * bitmap_w * bytes_per_texel;
    UByte *data = new UByte [bitmap_size];
    memset (data, 0, lightmap_size);
    cached_texture->data = data;
    cached_texture->bitmap = data + lightmap_size + margin_size; // Skip margin.
    cached_texture->size = bitmap_size;

    // Add new texture to cache.
    cached_texture->next = head;
    cached_texture->prev = NULL;
    if (head)
      head->prev = cached_texture;
    else
      tail = cached_texture;
    head = cached_texture;
  }
  return cached_texture;
}

void csTextureCacheSoftware::fill_texture (int MipMap, iPolygonTexture* pt,
  float u_min, float v_min, float u_max, float v_max)
{
  // Recalculate the lightmaps
  pt->RecalculateDynamicLights ();

  // Now cache the texture
  SoftwareCachedTexture *cached_texture = cache_texture (MipMap, pt);

  // Compute the rectangle on the lighted texture, if it is dirty
  (this->*create_lighted_texture) (pt, cached_texture, texman, u_min, v_min, u_max, v_max);
}

#define SysPrintf iG3D->System->Printf

void csTextureCacheSoftware::dump (csGraphics3DSoftwareCommon *iG3D)
{
  SysPrintf (MSG_CONSOLE, "Textures in the cache: %d\n", total_textures);
  SysPrintf (MSG_CONSOLE, "Total size: %ld bytes\n", total_size);
  int mean = (total_textures == 0) ? 0 : total_size / total_textures;
  SysPrintf (MSG_CONSOLE, "Bytes per texture: %d\n", mean);
}
<|endoftext|>


<commit_before>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "infill.h"
#include "functional"
#include "utils/polygonUtils.h"
#include "utils/logoutput.h"

namespace cura {

void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between)
{
    if (in_outline.size() == 0) return;
    if (line_distance == 0) return;
    const Polygons* outline = &in_outline;
    Polygons outline_offsetted;
    switch(pattern)
    {
    case EFillMethod::GRID:
        generateGridInfill(result_lines);
        break;
    case EFillMethod::LINES:
        generateLineInfill(result_lines, line_distance, fill_angle);
        break;
    case EFillMethod::TRIANGLES:
        generateTriangleInfill(result_lines);
        break;
    case EFillMethod::CONCENTRIC:
        PolygonUtils::offsetSafe(in_outline, outline_offset - infill_line_width / 2, infill_line_width, outline_offsetted, false); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
        outline = &outline_offsetted;
        if (abs(infill_line_width - line_distance) < 10)
        {
            generateConcentricInfillDense(*outline, result_polygons, in_between, remove_overlapping_perimeters);
        }
        else
        {
            generateConcentricInfill(*outline, result_polygons, line_distance);
        }
        break;
    case EFillMethod::ZIG_ZAG:
        generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
        break;
    default:
        logError("Fill pattern has unknown value.\n");
        break;
    }
}

void Infill::generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters)
{
    while(outline.size() > 0)
    {
        for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
        {
            PolygonRef r = outline[polyNr];
            result.add(r);
        }
        Polygons next_outline;
        PolygonUtils::offsetExtrusionWidth(outline, true, infill_line_width, next_outline, in_between, avoidOverlappingPerimeters);
        outline = next_outline;
    }

}

void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
{
    while(outline.size() > 0)
    {
        for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
        {
            PolygonRef r = outline[polyNr];
            result.add(r);
        }
        outline = outline.offset(-inset_value);
    }
}

void Infill::generateGridInfill(Polygons& result)
{
    generateLineInfill(result, line_distance * 2, fill_angle);
    generateLineInfill(result, line_distance * 2, fill_angle + 90);
}

void Infill::generateTriangleInfill(Polygons& result)
{
    generateLineInfill(result, line_distance * 3, fill_angle);
    generateLineInfill(result, line_distance * 3, fill_angle + 60);
    generateLineInfill(result, line_distance * 3, fill_angle + 120);
}

void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list)
{
    auto addLine = [&](Point from, Point to)
    {
        PolygonRef p = result.newPoly();
        p.add(rotation_matrix.unapply(from));
        p.add(rotation_matrix.unapply(to));
    };

    auto compare_int64_t = [](const void* a, const void* b)
    {
        int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
        if (n < 0)
        {
            return -1;
        }
        if (n > 0)
        {
            return 1;
        }
        return 0;
    };

    int scanline_idx = 0;
    for(int64_t x = scanline_min_idx * line_distance; x < boundary.max.X; x += line_distance)
    {
        std::vector<int64_t>& crossings = cut_list[scanline_idx];
        qsort(crossings.data(), crossings.size(), sizeof(int64_t), compare_int64_t);
        for(unsigned int crossing_idx = 0; crossing_idx + 1 < crossings.size(); crossing_idx += 2)
        {
            if (crossings[crossing_idx + 1] - crossings[crossing_idx] < infill_line_width / 5)
            { // segment is too short to create infill
                continue;
            }
            addLine(Point(x, crossings[crossing_idx]), Point(x, crossings[crossing_idx + 1]));
        }
        scanline_idx += 1;
    }
}

void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle)
{
    PointMatrix rotation_matrix(fill_angle);
    NoZigZagConnectorProcessor lines_processor(rotation_matrix, result);
    bool connected_zigzags = false;
    bool safe_outline_offset = false;
    generateLinearBasedInfill(outline_offset, safe_outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags);
}

void Infill::generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces)
{
    bool safe_outline_offset = true;

    PointMatrix rotation_matrix(fill_angle);
    if (use_endpieces)
    {
        if (connected_zigzags)
        {
            ZigzagConnectorProcessorConnectedEndPieces zigzag_processor(rotation_matrix, result);
            generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
        }
        else
        {
            ZigzagConnectorProcessorDisconnectedEndPieces zigzag_processor(rotation_matrix, result);
            generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
        }
    }
    else
    {
        ZigzagConnectorProcessorNoEndPieces zigzag_processor(rotation_matrix, result);
        generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
    }
}

/*
 * algorithm:
 * 1. for each line segment of each polygon:
 *      store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
 *      (zigzag): add boundary segments to result
 * 2. for each scanline:
 *      sort the associated intersections
 *      and connect them using the even-odd rule
 *
 * rough explanation of the zigzag algorithm:
 * while walking around (each) polygon (1.)
 *  if polygon intersects with even scanline
 *      start boundary segment (add each following segment to the [result])
 *  when polygon intersects with a scanline again
 *      stop boundary segment (stop adding segments to the [result])
 *  (see infill/ZigzagConnectorProcessor.h for actual implementation details)
 *
 *
 * we call the areas between two consecutive scanlines a 'scansegment'.
 * Scansegment x is the area between scanline x and scanline x+1
 * Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment,
 *  while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
 */
void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags)
{
    if (line_distance == 0)
    {
        return;
    }
    if (in_outline.size() == 0)
    {
        return;
    }

    Polygons outline;
    if (outline_offset != 0)
    {
        PolygonUtils::offsetSafe(in_outline, outline_offset, infill_line_width, outline, remove_overlapping_perimeters && safe_outline_offset);
    }
    else
    {
        outline = in_outline;
    }

        outline = outline.offset(infill_overlap);

    if (outline.size() == 0)
    {
        return;
    }

    outline.applyMatrix(rotation_matrix);

    AABB boundary(outline);

    int scanline_min_idx = boundary.min.X / line_distance;
    int line_count = (boundary.max.X + (line_distance - 1)) / line_distance - scanline_min_idx;

    std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments

    for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++)
    {
        cut_list.push_back(std::vector<int64_t>());
    }

    for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++)
    {
        PolygonRef poly = outline[poly_idx];
        Point p0 = poly.back();
        zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type
        for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
        {
            Point p1 = poly[point_idx];
            if (p1.X == p0.X)
            {
                zigzag_connector_processor.registerVertex(p1);
                // TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors)
                // note: this is already a problem for normal infill, but hasn't really cothered anyone so far.
                p0 = p1;
                continue;
            }

            int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -line_distance)) / line_distance; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1   ...
            int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -line_distance)) / line_distance; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
            // this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
            // in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
            // otherwise only 1 will be created, counting as an actual intersection
            int direction = 1;
            if (p0.X > p1.X)
            {
                direction = -1;
                scanline_idx1 += 1; // only consider the scanlines in between the scansegments
            }
            else
            {
                scanline_idx0 += 1; // only consider the scanlines in between the scansegments
            }

            for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
            {
                int x = scanline_idx * line_distance;
                int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
                cut_list[scanline_idx - scanline_min_idx].push_back(y);
                Point scanline_linesegment_intersection(x, y);
                zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
            }
            zigzag_connector_processor.registerVertex(p1);
            p0 = p1;
        }
        zigzag_connector_processor.registerPolyFinished();
    }

    if (cut_list.size() == 0)
    {
        return;
    }
    if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2)
    {
        return;  // don't add connection if boundary already contains whole outline!
    }

    addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list);
}

}//namespace cura
<commit_msg>fix: don't recalculate line distance in the engine (CURA-1317)<commit_after>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "infill.h"
#include "functional"
#include "utils/polygonUtils.h"
#include "utils/logoutput.h"

namespace cura {

void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between)
{
    if (in_outline.size() == 0) return;
    if (line_distance == 0) return;
    const Polygons* outline = &in_outline;
    Polygons outline_offsetted;
    switch(pattern)
    {
    case EFillMethod::GRID:
        generateGridInfill(result_lines);
        break;
    case EFillMethod::LINES:
        generateLineInfill(result_lines, line_distance, fill_angle);
        break;
    case EFillMethod::TRIANGLES:
        generateTriangleInfill(result_lines);
        break;
    case EFillMethod::CONCENTRIC:
        PolygonUtils::offsetSafe(in_outline, outline_offset - infill_line_width / 2, infill_line_width, outline_offsetted, false); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
        outline = &outline_offsetted;
        if (abs(infill_line_width - line_distance) < 10)
        {
            generateConcentricInfillDense(*outline, result_polygons, in_between, remove_overlapping_perimeters);
        }
        else
        {
            generateConcentricInfill(*outline, result_polygons, line_distance);
        }
        break;
    case EFillMethod::ZIG_ZAG:
        generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
        break;
    default:
        logError("Fill pattern has unknown value.\n");
        break;
    }
}

void Infill::generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters)
{
    while(outline.size() > 0)
    {
        for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
        {
            PolygonRef r = outline[polyNr];
            result.add(r);
        }
        Polygons next_outline;
        PolygonUtils::offsetExtrusionWidth(outline, true, infill_line_width, next_outline, in_between, avoidOverlappingPerimeters);
        outline = next_outline;
    }

}

void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
{
    while(outline.size() > 0)
    {
        for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
        {
            PolygonRef r = outline[polyNr];
            result.add(r);
        }
        outline = outline.offset(-inset_value);
    }
}

void Infill::generateGridInfill(Polygons& result)
{
    generateLineInfill(result, line_distance, fill_angle);
    generateLineInfill(result, line_distance, fill_angle + 90);
}

void Infill::generateTriangleInfill(Polygons& result)
{
    generateLineInfill(result, line_distance, fill_angle);
    generateLineInfill(result, line_distance, fill_angle + 60);
    generateLineInfill(result, line_distance, fill_angle + 120);
}

void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list)
{
    auto addLine = [&](Point from, Point to)
    {
        PolygonRef p = result.newPoly();
        p.add(rotation_matrix.unapply(from));
        p.add(rotation_matrix.unapply(to));
    };

    auto compare_int64_t = [](const void* a, const void* b)
    {
        int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
        if (n < 0)
        {
            return -1;
        }
        if (n > 0)
        {
            return 1;
        }
        return 0;
    };

    int scanline_idx = 0;
    for(int64_t x = scanline_min_idx * line_distance; x < boundary.max.X; x += line_distance)
    {
        std::vector<int64_t>& crossings = cut_list[scanline_idx];
        qsort(crossings.data(), crossings.size(), sizeof(int64_t), compare_int64_t);
        for(unsigned int crossing_idx = 0; crossing_idx + 1 < crossings.size(); crossing_idx += 2)
        {
            if (crossings[crossing_idx + 1] - crossings[crossing_idx] < infill_line_width / 5)
            { // segment is too short to create infill
                continue;
            }
            addLine(Point(x, crossings[crossing_idx]), Point(x, crossings[crossing_idx + 1]));
        }
        scanline_idx += 1;
    }
}

void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle)
{
    PointMatrix rotation_matrix(fill_angle);
    NoZigZagConnectorProcessor lines_processor(rotation_matrix, result);
    bool connected_zigzags = false;
    bool safe_outline_offset = false;
    generateLinearBasedInfill(outline_offset, safe_outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags);
}

void Infill::generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces)
{
    bool safe_outline_offset = true;

    PointMatrix rotation_matrix(fill_angle);
    if (use_endpieces)
    {
        if (connected_zigzags)
        {
            ZigzagConnectorProcessorConnectedEndPieces zigzag_processor(rotation_matrix, result);
            generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
        }
        else
        {
            ZigzagConnectorProcessorDisconnectedEndPieces zigzag_processor(rotation_matrix, result);
            generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
        }
    }
    else
    {
        ZigzagConnectorProcessorNoEndPieces zigzag_processor(rotation_matrix, result);
        generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
    }
}

/*
 * algorithm:
 * 1. for each line segment of each polygon:
 *      store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
 *      (zigzag): add boundary segments to result
 * 2. for each scanline:
 *      sort the associated intersections
 *      and connect them using the even-odd rule
 *
 * rough explanation of the zigzag algorithm:
 * while walking around (each) polygon (1.)
 *  if polygon intersects with even scanline
 *      start boundary segment (add each following segment to the [result])
 *  when polygon intersects with a scanline again
 *      stop boundary segment (stop adding segments to the [result])
 *  (see infill/ZigzagConnectorProcessor.h for actual implementation details)
 *
 *
 * we call the areas between two consecutive scanlines a 'scansegment'.
 * Scansegment x is the area between scanline x and scanline x+1
 * Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment,
 *  while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
 */
void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags)
{
    if (line_distance == 0)
    {
        return;
    }
    if (in_outline.size() == 0)
    {
        return;
    }

    Polygons outline;
    if (outline_offset != 0)
    {
        PolygonUtils::offsetSafe(in_outline, outline_offset, infill_line_width, outline, remove_overlapping_perimeters && safe_outline_offset);
    }
    else
    {
        outline = in_outline;
    }

        outline = outline.offset(infill_overlap);

    if (outline.size() == 0)
    {
        return;
    }

    outline.applyMatrix(rotation_matrix);

    AABB boundary(outline);

    int scanline_min_idx = boundary.min.X / line_distance;
    int line_count = (boundary.max.X + (line_distance - 1)) / line_distance - scanline_min_idx;

    std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments

    for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++)
    {
        cut_list.push_back(std::vector<int64_t>());
    }

    for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++)
    {
        PolygonRef poly = outline[poly_idx];
        Point p0 = poly.back();
        zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type
        for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
        {
            Point p1 = poly[point_idx];
            if (p1.X == p0.X)
            {
                zigzag_connector_processor.registerVertex(p1);
                // TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors)
                // note: this is already a problem for normal infill, but hasn't really cothered anyone so far.
                p0 = p1;
                continue;
            }

            int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -line_distance)) / line_distance; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1   ...
            int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -line_distance)) / line_distance; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
            // this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
            // in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
            // otherwise only 1 will be created, counting as an actual intersection
            int direction = 1;
            if (p0.X > p1.X)
            {
                direction = -1;
                scanline_idx1 += 1; // only consider the scanlines in between the scansegments
            }
            else
            {
                scanline_idx0 += 1; // only consider the scanlines in between the scansegments
            }

            for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
            {
                int x = scanline_idx * line_distance;
                int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
                cut_list[scanline_idx - scanline_min_idx].push_back(y);
                Point scanline_linesegment_intersection(x, y);
                zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
            }
            zigzag_connector_processor.registerVertex(p1);
            p0 = p1;
        }
        zigzag_connector_processor.registerPolyFinished();
    }

    if (cut_list.size() == 0)
    {
        return;
    }
    if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2)
    {
        return;  // don't add connection if boundary already contains whole outline!
    }

    addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list);
}

}//namespace cura
<|endoftext|>


<commit_before>/*!
 * \author David
 * \date 29-Apr-16.
 */

#include <algorithm>
#include <cmath>
#include <easylogging++.h>
#include "texture_manager.h"
#include "../../vulkan/render_context.h"
#include "../../../mc_interface/mc_objects.h"

namespace nova {
    texture_manager::texture_manager(std::shared_ptr<render_context> context) : context(context) {
        LOG(INFO) << "Creating the Texture Manager";
        reset();
        LOG(INFO) << "Texture manager created";
    }

    texture_manager::~texture_manager() {
        // gotta free up all the Vulkan textures
        reset();
    }

    void texture_manager::reset() {
        if(!atlases.empty()) {
            // Nothing to deallocate, let's just return
            return;
        }

        for(auto& tex : atlases) {
            tex.second.destroy();
        }

        atlases.clear();
        locations.clear();

        atlases["lightmap"] = texture2D(vk::Extent2D{16, 16}, vk::Format::eR8G8B8A8Unorm, context);
        atlases["lightmap"].set_name("lightmap");
        LOG(INFO) << "Created lightmap";

        clear_dynamic_textures();
    }

    void texture_manager::add_texture(mc_atlas_texture &new_texture) {
        LOG(INFO) << "Adding texture " << new_texture.name << " (" << new_texture.width << "x" << new_texture.height << ")";
        std::string texture_name = new_texture.name;
        LOG(TRACE) << "Saved texture name";
        auto dimensions = vk::Extent2D{new_texture.width, new_texture.height};
        texture2D texture{dimensions, vk::Format::eR8G8B8A8Unorm, context};
        LOG(TRACE) << "Created texture object";
        texture.set_name(texture_name);

        std::vector<uint8_t> pixel_data((std::size_t) (new_texture.width * new_texture.height * new_texture.num_components));
        for(uint32_t i = 0; i < new_texture.width * new_texture.height * new_texture.num_components; i++) {
            pixel_data[i] = (uint8_t) new_texture.texture_data[i];
        }

        LOG(TRACE) << "Added pixel data to buffer";

        texture.set_data(pixel_data.data(), dimensions);

        LOG(TRACE) << "Sent texture data to GPU";

        atlases[texture_name] = texture;
        LOG(DEBUG) << "Texture atlas " << texture_name << " is Vulkan texture " << texture.get_vk_image();
    }

    void texture_manager::add_texture_location(mc_texture_atlas_location &location) {
        texture_location tex_loc = {
                { location.min_u, location.min_v },
                { location.max_u, location.max_v }
        };

        locations[location.name] = tex_loc;
    }

    const texture_manager::texture_location texture_manager::get_texture_location(const std::string &texture_name) {
        // If we haven't explicitly added a texture location for this texture, let's just assume that the texture isn't
        // in an atlas and thus covers the whole (0 - 1) UV space

        if(locations.find(texture_name) != locations.end()) {
            return locations[texture_name];

        } else {
            return {{0, 0}, {1, 1}};
        }
    }

    texture2D &texture_manager::get_texture(std::string texture_name) {
        if(atlases.find(texture_name) != atlases.end()) {
            return atlases.at(texture_name);
        }

        if(dynamic_tex_name_to_idx.find(texture_name) != dynamic_tex_name_to_idx.end()) {
            auto idx = dynamic_tex_name_to_idx.at(texture_name);
            if(dynamic_textures.size() > idx) {
                return dynamic_textures.at(idx);
            }
        }

        LOG(ERROR) << "Could not find texture " << texture_name;
        throw std::domain_error("Could not find texture " + texture_name);
    }

    int texture_manager::get_max_texture_size() {
        if(max_texture_size < 0) {
            max_texture_size = context->gpu.props.limits.maxImageDimension2D;

			LOG(DEBUG) << "max texturesize reported by gpu: " << max_texture_size;
        }
        return max_texture_size;
    }

    // Implementation based on RenderGraph::build_aliases from the Granite engine
    void texture_manager::create_dynamic_textures(const std::unordered_map<std::string, texture_resource> &textures,
                                                  const std::vector<render_pass> &passes, std::shared_ptr<swapchain_manager> swapchain) {
        // For each texture in the passes, try to assign it to an existing resource
        // We'll basically create a list of which texture resources can be assigned to each physical resource
        // We want to alias textures. We can alias texture A and B if all reads from A finish before all writes to B AND
        // if A and B have the same format and dimension
        // Maybe we should make a list of things with the same format and dimension?

        clear_dynamic_textures();

        struct range {
            uint32_t first_write_pass = ~0u;
            uint32_t last_write_pass = 0;
            uint32_t first_read_pass = ~0u;
            uint32_t last_read_pass = 0;

            bool has_writer() const
            {
                return first_write_pass <= last_write_pass;
            }

            bool has_reader() const
            {
                return first_read_pass <= last_read_pass;
            }

            bool is_used() const
            {
                return has_writer() || has_reader();
            }

            bool can_alias() const
            {
                // If we read before we have completely written to a resource we need to preserve it, so no alias is possible.
                return !(has_reader() && has_writer() && first_read_pass <= first_write_pass);
            }

            unsigned last_used_pass() const
            {
                unsigned last_pass = 0;
                if (has_writer())
                    last_pass = std::max(last_pass, last_write_pass);
                if (has_reader())
                    last_pass = std::max(last_pass, last_read_pass);
                return last_pass;
            }

            unsigned first_used_pass() const
            {
                unsigned first_pass = ~0u;
                if (has_writer())
                    first_pass = std::min(first_pass, first_write_pass);
                if (has_reader())
                    first_pass = std::min(first_pass, first_read_pass);
                return first_pass;
            }

            bool is_disjoint_with(const range& other) const {
                if (!is_used() || !other.is_used())
                    return false;
                if (!can_alias() || !other.can_alias())
                    return false;

                bool left = last_used_pass() < other.first_used_pass();
                bool right = other.last_used_pass() < first_used_pass();
                return left || right;
            }
        };

        // Look at what range of render passes each resource is used in
        std::unordered_map<std::string, range> resource_used_range;
        std::vector<std::string> resources_in_order;

        uint32_t pass_idx = 0;
        for(const auto& pass : passes) {
            if(pass.texture_inputs) {
                for(const auto &input : pass.texture_inputs.value()) {
                    auto& tex_range = resource_used_range[input];

                    if(pass_idx < tex_range.first_write_pass) {
                        tex_range.first_write_pass = pass_idx;

                    } else if(pass_idx > tex_range.last_write_pass) {
                        tex_range.last_write_pass = pass_idx;
                    }

                    if(std::find(resources_in_order.begin(), resources_in_order.end(), input) == resources_in_order.end()) {
                        resources_in_order.push_back(input);
                    }
                }
            }

            if(pass.texture_outputs) {
                for(const auto &output : pass.texture_outputs.value()) {
                    auto& tex_range = resource_used_range[output];

                    if(pass_idx < tex_range.first_write_pass) {
                        tex_range.first_write_pass = pass_idx;

                    } else if(pass_idx > tex_range.last_write_pass) {
                        tex_range.last_write_pass = pass_idx;
                    }

                    if(std::find(resources_in_order.begin(), resources_in_order.end(), output) == resources_in_order.end()) {
                        resources_in_order.push_back(output);
                    }
                }
            }

            pass_idx++;
        }

        LOG(INFO) << "Ordered resources";

        // Figure out which resources can be aliased
        std::unordered_map<std::string, std::string> aliases;

        for(size_t i = 0; i < resources_in_order.size(); i++) {
            const auto& to_alias_name = resources_in_order[i];
            LOG(INFO) << "Determining if we can alias `" << to_alias_name << "`. Does it exist? " << (textures.find(to_alias_name) != textures.end());
            const auto& to_alias_format = textures.at(to_alias_name).format;

            // Only try to alias with lower-indexed resources
            for(size_t j = 0; j < i; j++) {
                LOG(INFO) << "Trying to alias it with rexource at index " << j << " out of " << resources_in_order.size();
                const auto& try_alias_name = resources_in_order[j];
                if(resource_used_range[to_alias_name].is_disjoint_with(resource_used_range[try_alias_name])) {
                    // They can be aliased if they have the same format
                    const auto& try_alias_format = textures.at(try_alias_name).format;
                    if(to_alias_format == try_alias_format) {
                        aliases[to_alias_name] = try_alias_name;
                    }
                }
            }
        }

        LOG(INFO) << "Figured out which resources can be aliased";

        auto swapchain_dimensions = swapchain->get_swapchain_extent();

        // For each texture:
        //  - If it isn't in the aliases map, create a new texture with its format and add it to the textures map
        //  - If it is in the aliases map, follow its chain of aliases

        for(const auto& named_texture : textures) {
            std::string texture_name = named_texture.first;
            while(aliases.find(texture_name) != aliases.end()) {
                LOG(INFO) << "Resource " << texture_name << " is aliased with " << aliases[texture_name];
                texture_name = aliases[texture_name];
            }

            // We've found the first texture in this alias chain - let's create an actual texture for it if needed
            if(dynamic_tex_name_to_idx.find(texture_name) != dynamic_tex_name_to_idx.end()) {
                LOG(INFO) << "Need to create it";
                // The texture we're all aliasing doesn't have a real texture yet. Let's fix that
                const texture_format& format = textures.at(texture_name).format;

                vk::Extent2D dimensions;
                if(format.dimension_type == texture_dimension_type_enum::Absolute) {
                    dimensions = vk::Extent2D{static_cast<uint32_t>(format.width), static_cast<uint32_t>(format.height)};

                } else {
                    dimensions = swapchain_dimensions;
                    dimensions.width *= format.width;
                    dimensions.height *= format.height;
                }

                auto pixel_format = get_vk_format_from_pixel_format(format.pixel_format);
                auto tex = texture2D{dimensions, pixel_format, context};

                auto new_tex_index = dynamic_textures.size();
                dynamic_textures.push_back(tex);
                dynamic_tex_name_to_idx[texture_name] = new_tex_index;
                dynamic_tex_name_to_idx[named_texture.first] = new_tex_index;

            } else {
                LOG(INFO) << "The physical resource already exists, so we're just gonna use that";
                // The texture we're aliasing already has a real texture behind it - so let's use that
                dynamic_tex_name_to_idx[named_texture.first] = dynamic_tex_name_to_idx[texture_name];
            }
        }
    }

    void texture_manager::clear_dynamic_textures() {
        dynamic_textures.resize(0);
        dynamic_tex_name_to_idx.erase(dynamic_tex_name_to_idx.begin(), dynamic_tex_name_to_idx.end());
    }

    bool texture_manager::is_texture_known(const std::string &texture_name) const {
        if(atlases.find(texture_name) != atlases.end()) {
            return true;
        }

        return dynamic_tex_name_to_idx.find(texture_name) != dynamic_tex_name_to_idx.end();

    }

    vk::Format get_vk_format_from_pixel_format(pixel_format_enum format) {
        switch(format) {
            case pixel_format_enum::RGB8:
                return vk::Format::eR8G8B8Unorm;
            case pixel_format_enum::RGBA8:
                return vk::Format::eR8G8B8A8Unorm;
            case pixel_format_enum::RGB16F:
                return vk::Format::eR16G16B16Sfloat;
            case pixel_format_enum::RGBA16F:
                return vk::Format::eR16G16B16A16Sfloat;
            case pixel_format_enum::RGB32F:
                return vk::Format::eR32G32B32Sfloat;
            case pixel_format_enum::RGBA32F:
                return vk::Format::eR32G32B32A32Sfloat;
            case pixel_format_enum::Depth:
                return vk::Format::eD32Sfloat;
            case pixel_format_enum::DepthStencil:
                return vk::Format::eD24UnormS8Uint;
            default:
                LOG(WARNING) << "Could not determine Vulkan format for pixel format " << pixel_format_enum::to_string(format);
                return vk::Format::eR8G8B8Unorm;
        }
    }
}
<commit_msg>No more flkickering!<commit_after>/*!
 * \author David
 * \date 29-Apr-16.
 */

#include <algorithm>
#include <cmath>
#include <easylogging++.h>
#include "texture_manager.h"
#include "../../vulkan/render_context.h"
#include "../../../mc_interface/mc_objects.h"

namespace nova {
    texture_manager::texture_manager(std::shared_ptr<render_context> context) : context(context) {
        LOG(INFO) << "Creating the Texture Manager";
        reset();
        LOG(INFO) << "Texture manager created";
    }

    texture_manager::~texture_manager() {
        // gotta free up all the Vulkan textures
        reset();
    }

    void texture_manager::reset() {
        if(!atlases.empty()) {
            // Nothing to deallocate, let's just return
            return;
        }

        for(auto& tex : atlases) {
            tex.second.destroy();
        }

        atlases.clear();
        locations.clear();

        atlases["lightmap"] = texture2D(vk::Extent2D{16, 16}, vk::Format::eR8G8B8A8Unorm, context);
        atlases["lightmap"].set_name("lightmap");
        LOG(INFO) << "Created lightmap";

        clear_dynamic_textures();
    }

    void texture_manager::add_texture(mc_atlas_texture &new_texture) {
        LOG(INFO) << "Adding texture " << new_texture.name << " (" << new_texture.width << "x" << new_texture.height << ")";
        std::string texture_name = new_texture.name;
        LOG(TRACE) << "Saved texture name";
        auto dimensions = vk::Extent2D{new_texture.width, new_texture.height};
        texture2D texture{dimensions, vk::Format::eR8G8B8A8Unorm, context};
        LOG(TRACE) << "Created texture object";
        texture.set_name(texture_name);

        std::vector<uint8_t> pixel_data((std::size_t) (new_texture.width * new_texture.height * new_texture.num_components));
        for(uint32_t i = 0; i < new_texture.width * new_texture.height * new_texture.num_components; i++) {
            pixel_data[i] = (uint8_t) new_texture.texture_data[i];
        }

        LOG(TRACE) << "Added pixel data to buffer";

        texture.set_data(pixel_data.data(), dimensions);

        LOG(TRACE) << "Sent texture data to GPU";

        atlases[texture_name] = texture;
        LOG(DEBUG) << "Texture atlas " << texture_name << " is Vulkan texture " << texture.get_vk_image();
    }

    void texture_manager::add_texture_location(mc_texture_atlas_location &location) {
        texture_location tex_loc = {
                { location.min_u, location.min_v },
                { location.max_u, location.max_v }
        };

        locations[location.name] = tex_loc;
    }

    const texture_manager::texture_location texture_manager::get_texture_location(const std::string &texture_name) {
        // If we haven't explicitly added a texture location for this texture, let's just assume that the texture isn't
        // in an atlas and thus covers the whole (0 - 1) UV space

        if(locations.find(texture_name) != locations.end()) {
            return locations[texture_name];

        } else {
            return {{0, 0}, {1, 1}};
        }
    }

    texture2D &texture_manager::get_texture(std::string texture_name) {
        if(atlases.find(texture_name) != atlases.end()) {
            return atlases.at(texture_name);
        }

        LOG(INFO) << "Checking if texture " << texture_name << " is in the dynamic textures. There's " << dynamic_tex_name_to_idx.size() << " dynamic textures, it should be one of them";
        if(dynamic_tex_name_to_idx.find(texture_name) != dynamic_tex_name_to_idx.end()) {
            auto idx = dynamic_tex_name_to_idx.at(texture_name);
            if(dynamic_textures.size() > idx) {
                return dynamic_textures.at(idx);
            }
        }

        LOG(ERROR) << "Could not find texture " << texture_name;
        throw std::domain_error("Could not find texture " + texture_name);
    }

    int texture_manager::get_max_texture_size() {
        if(max_texture_size < 0) {
            max_texture_size = context->gpu.props.limits.maxImageDimension2D;

			LOG(DEBUG) << "max texturesize reported by gpu: " << max_texture_size;
        }
        return max_texture_size;
    }

    // Implementation based on RenderGraph::build_aliases from the Granite engine
    void texture_manager::create_dynamic_textures(const std::unordered_map<std::string, texture_resource> &textures,
                                                  const std::vector<render_pass> &passes, std::shared_ptr<swapchain_manager> swapchain) {
        // For each texture in the passes, try to assign it to an existing resource
        // We'll basically create a list of which texture resources can be assigned to each physical resource
        // We want to alias textures. We can alias texture A and B if all reads from A finish before all writes to B AND
        // if A and B have the same format and dimension
        // Maybe we should make a list of things with the same format and dimension?

        clear_dynamic_textures();

        struct range {
            uint32_t first_write_pass = ~0u;
            uint32_t last_write_pass = 0;
            uint32_t first_read_pass = ~0u;
            uint32_t last_read_pass = 0;

            bool has_writer() const
            {
                return first_write_pass <= last_write_pass;
            }

            bool has_reader() const
            {
                return first_read_pass <= last_read_pass;
            }

            bool is_used() const
            {
                return has_writer() || has_reader();
            }

            bool can_alias() const
            {
                // If we read before we have completely written to a resource we need to preserve it, so no alias is possible.
                return !(has_reader() && has_writer() && first_read_pass <= first_write_pass);
            }

            unsigned last_used_pass() const
            {
                unsigned last_pass = 0;
                if (has_writer())
                    last_pass = std::max(last_pass, last_write_pass);
                if (has_reader())
                    last_pass = std::max(last_pass, last_read_pass);
                return last_pass;
            }

            unsigned first_used_pass() const
            {
                unsigned first_pass = ~0u;
                if (has_writer())
                    first_pass = std::min(first_pass, first_write_pass);
                if (has_reader())
                    first_pass = std::min(first_pass, first_read_pass);
                return first_pass;
            }

            bool is_disjoint_with(const range& other) const {
                if (!is_used() || !other.is_used())
                    return false;
                if (!can_alias() || !other.can_alias())
                    return false;

                bool left = last_used_pass() < other.first_used_pass();
                bool right = other.last_used_pass() < first_used_pass();
                return left || right;
            }
        };

        // Look at what range of render passes each resource is used in
        std::unordered_map<std::string, range> resource_used_range;
        std::vector<std::string> resources_in_order;

        uint32_t pass_idx = 0;
        for(const auto& pass : passes) {
            if(pass.texture_inputs) {
                for(const auto &input : pass.texture_inputs.value()) {
                    auto& tex_range = resource_used_range[input];

                    if(pass_idx < tex_range.first_write_pass) {
                        tex_range.first_write_pass = pass_idx;

                    } else if(pass_idx > tex_range.last_write_pass) {
                        tex_range.last_write_pass = pass_idx;
                    }

                    if(std::find(resources_in_order.begin(), resources_in_order.end(), input) == resources_in_order.end()) {
                        resources_in_order.push_back(input);
                    }
                }
            }

            if(pass.texture_outputs) {
                for(const auto &output : pass.texture_outputs.value()) {
                    auto& tex_range = resource_used_range[output];

                    if(pass_idx < tex_range.first_write_pass) {
                        tex_range.first_write_pass = pass_idx;

                    } else if(pass_idx > tex_range.last_write_pass) {
                        tex_range.last_write_pass = pass_idx;
                    }

                    if(std::find(resources_in_order.begin(), resources_in_order.end(), output) == resources_in_order.end()) {
                        resources_in_order.push_back(output);
                    }
                }
            }

            pass_idx++;
        }

        LOG(INFO) << "Ordered resources";

        // Figure out which resources can be aliased
        std::unordered_map<std::string, std::string> aliases;

        for(size_t i = 0; i < resources_in_order.size(); i++) {
            const auto& to_alias_name = resources_in_order[i];
            LOG(INFO) << "Determining if we can alias `" << to_alias_name << "`. Does it exist? " << (textures.find(to_alias_name) != textures.end());
            const auto& to_alias_format = textures.at(to_alias_name).format;

            // Only try to alias with lower-indexed resources
            for(size_t j = 0; j < i; j++) {
                LOG(INFO) << "Trying to alias it with rexource at index " << j << " out of " << resources_in_order.size();
                const auto& try_alias_name = resources_in_order[j];
                if(resource_used_range[to_alias_name].is_disjoint_with(resource_used_range[try_alias_name])) {
                    // They can be aliased if they have the same format
                    const auto& try_alias_format = textures.at(try_alias_name).format;
                    if(to_alias_format == try_alias_format) {
                        aliases[to_alias_name] = try_alias_name;
                    }
                }
            }
        }

        LOG(INFO) << "Figured out which resources can be aliased";

        auto swapchain_dimensions = swapchain->get_swapchain_extent();

        // For each texture:
        //  - If it isn't in the aliases map, create a new texture with its format and add it to the textures map
        //  - If it is in the aliases map, follow its chain of aliases

        for(const auto& named_texture : textures) {
            std::string texture_name = named_texture.first;
            while(aliases.find(texture_name) != aliases.end()) {
                LOG(INFO) << "Resource " << texture_name << " is aliased with " << aliases[texture_name];
                texture_name = aliases[texture_name];
            }

            // We've found the first texture in this alias chain - let's create an actual texture for it if needed
            if(dynamic_tex_name_to_idx.find(texture_name) == dynamic_tex_name_to_idx.end()) {
                LOG(INFO) << "Need to create it";
                // The texture we're all aliasing doesn't have a real texture yet. Let's fix that
                const texture_format& format = textures.at(texture_name).format;

                vk::Extent2D dimensions;
                if(format.dimension_type == texture_dimension_type_enum::Absolute) {
                    dimensions = vk::Extent2D{static_cast<uint32_t>(format.width), static_cast<uint32_t>(format.height)};

                } else {
                    dimensions = swapchain_dimensions;
                    dimensions.width *= format.width;
                    dimensions.height *= format.height;
                }

                auto pixel_format = get_vk_format_from_pixel_format(format.pixel_format);
                auto tex = texture2D{dimensions, pixel_format, context};

                auto new_tex_index = dynamic_textures.size();
                dynamic_textures.push_back(tex);
                dynamic_tex_name_to_idx[texture_name] = new_tex_index;
                //dynamic_tex_name_to_idx[named_texture.first] = new_tex_index;

                LOG(INFO) << "Added texture " << tex.get_name() << " to the dynamic textures";
                LOG(INFO) << "set dynamic_texture_to_idx[" << texture_name << "] = " << new_tex_index;
                //LOG(INFO) << "dynamic_texture_to_idx[" << named_texture.first << "] = " << new_tex_index;

            } else {
                LOG(INFO) << "The physical resource already exists, so we're just gonna use that";
                // The texture we're aliasing already has a real texture behind it - so let's use that
                dynamic_tex_name_to_idx[named_texture.first] = dynamic_tex_name_to_idx[texture_name];
            }
        }
    }

    void texture_manager::clear_dynamic_textures() {
        LOG(INFO) << "Cleared dynamic textures";
        dynamic_textures.resize(0);
        dynamic_tex_name_to_idx.erase(dynamic_tex_name_to_idx.begin(), dynamic_tex_name_to_idx.end());
    }

    bool texture_manager::is_texture_known(const std::string &texture_name) const {
        if(atlases.find(texture_name) != atlases.end()) {
            return true;
        }

        return dynamic_tex_name_to_idx.find(texture_name) != dynamic_tex_name_to_idx.end();

    }

    vk::Format get_vk_format_from_pixel_format(pixel_format_enum format) {
        switch(format) {
            case pixel_format_enum::RGB8:
                return vk::Format::eR8G8B8Unorm;
            case pixel_format_enum::RGBA8:
                return vk::Format::eR8G8B8A8Unorm;
            case pixel_format_enum::RGB16F:
                return vk::Format::eR16G16B16Sfloat;
            case pixel_format_enum::RGBA16F:
                return vk::Format::eR16G16B16A16Sfloat;
            case pixel_format_enum::RGB32F:
                return vk::Format::eR32G32B32Sfloat;
            case pixel_format_enum::RGBA32F:
                return vk::Format::eR32G32B32A32Sfloat;
            case pixel_format_enum::Depth:
                return vk::Format::eD32Sfloat;
            case pixel_format_enum::DepthStencil:
                return vk::Format::eD24UnormS8Uint;
            default:
                LOG(WARNING) << "Could not determine Vulkan format for pixel format " << pixel_format_enum::to_string(format);
                return vk::Format::eR8G8B8Unorm;
        }
    }
}
<|endoftext|>


<commit_before>#include<string.h>
#include<limits.h>
#include<stack>
#include<iostream>
using namespace std;

bool isPalindrome(const char *s, int n){

    if(NULL==s || n<1){
        return false;
    }

    const char* front, *back;

    front = s;
    back = s+n-1;

    while(front<back){
        if(*front!=*back) return false;

        ++front;
        --back;
    }

    return true;
}

bool strBInA(const char* a, const char* b){
    long hash = 0;

    while(*a){
        hash |= 1<< (*a-'A');
        ++a;
    }

    while(*b){
        if(!(hash&(1<<(*b-'A')))) return false;
        ++b;
    }

    return true;
}

void reverseWord(char* s, char* delim){

    char str[strlen(s)];

    strcpy(str, s);

    char *str02 = NULL;
    str02 = strtok(str, delim);

    stack<char*> rst;
    rst.push(str02);

    while(str02!=NULL){
        str02 = strtok(NULL, delim);

        if(NULL != str02) rst.push(str02);
    }

    int n=rst.size();
    for(int i=0;i<n;i++){
        cout << rst.top() << ", ";
        rst.pop();
    }
    cout << endl;
}

int atoi_01(char* s){

    if(NULL == s) return 0;
    while(isspace(*s)) ++s;

    int sign = 1;
    if(s[0]=='+' || s[0] == '-'){
        if(s[0] == '-') sign=-1;
        ++s;
    }

    int n=0,c=0;
    while(*s!=0){
        if(!isdigit(*s)) return 0;
        c=*s-'0';

        if(sign==1 && (n>=INT_MAX/10 || (n==INT_MAX/10 && c>INT_MAX%10))){
            n=INT_MAX;
            break;
        }else if(sign==-1 && (n>=INT_MAX/10 || (n==INT_MAX/10 && c-1>INT_MAX%10))){
            n=INT_MIN;
            break;
        }

       n=n*10+c;
        ++s;
    }

    return sign*n;
}

int main(){

    cout << "isPalindrome--------------------" << isPalindrome("madam", 5) << endl;
    cout << "strBInA-------------------------" << strBInA("abcdef","efa") << endl;

    // reverseWord("hello world.", " ");
    cout << "atoi_01--" << ((atoi_01("   +12345") == 12345) &&  (atoi_01("     -12345") == -12345)) << endl;
    cout << "INT_MAX:" << atoi_01("  1233424235345344") << " INT_MIN:" << atoi_01("   -347289489398549");
    return 0;
}

<commit_msg>strings all permutation<commit_after>#include<string.h>
#include<limits.h>
#include<stack>
#include<iostream>
using namespace std;

bool isPalindrome(const char *s, int n){

    if(NULL==s || n<1){
        return false;
    }

    const char* front, *back;

    front = s;
    back = s+n-1;

    while(front<back){
        if(*front!=*back) return false;

        ++front;
        --back;
    }

    return true;
}

bool strBInA(const char* a, const char* b){
    long hash = 0;

    while(*a){
        hash |= 1<< (*a-'A');
        ++a;
    }

    while(*b){
        if(!(hash&(1<<(*b-'A')))) return false;
        ++b;
    }

    return true;
}

void reverseWord(char* s, char* delim){

    char str[strlen(s)];

    strcpy(str, s);

    char *str02 = NULL;
    str02 = strtok(str, delim);

    stack<char*> rst;
    rst.push(str02);

    while(str02!=NULL){
        str02 = strtok(NULL, delim);

        if(NULL != str02) rst.push(str02);
    }

    int n=rst.size();
    for(int i=0;i<n;i++){
        cout << rst.top() << ", ";
        rst.pop();
    }
    cout << endl;
}

int atoi_01(char* s){

    if(NULL == s) return 0;
    while(isspace(*s)) ++s;

    int sign = 1;
    if(s[0]=='+' || s[0] == '-'){
        if(s[0] == '-') sign=-1;
        ++s;
    }

    int n=0,c=0;
    while(*s!=0){
        if(!isdigit(*s)) return 0;
        c=*s-'0';

        if(sign==1 && (n>=INT_MAX/10 || (n==INT_MAX/10 && c>INT_MAX%10))){
            n=INT_MAX;
            break;
        }else if(sign==-1 && (n>=INT_MAX/10 || (n==INT_MAX/10 && c-1>INT_MAX%10))){
            n=INT_MIN;
            break;
        }

       n=n*10+c;
        ++s;
    }

    return sign*n;
}

void allPermutation(char* s, int from, int to){

    if(from > to || to <= 1) return;

    if(from == to){
        for(int i=0;i<=to;i++) cout << s[i];
        cout << endl;
    }else{
        for(int i=from;i<=to;i++){
            swap(s[i], s[from]);
            allPermutation(s, from+1, to);
            swap(s[i], s[from]);
        }
    }
}

int main(){

    cout << "isPalindrome--------------------" << isPalindrome("madam", 5) << endl;
    cout << "strBInA-------------------------" << strBInA("abcdef","efa") << endl;

    // reverseWord("hello world.", " ");
    cout << "atoi_01--" << ((atoi_01("   +12345") == 12345) &&  (atoi_01("     -12345") == -12345)) << endl;
    cout << "INT_MAX:" << atoi_01("  1233424235345344") << " INT_MIN:" << atoi_01("   -347289489398549") << endl;

    char s0[3] = {'a', 'b', 'c'};
    allPermutation(s0, 0 ,2);
    return 0;
}

<|endoftext|>


<commit_before>/**
 * acljobs.cpp
 *
 * Copyright (c) 2004 David Faure <faure@kde.org>
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of this program with any edition of
 *  the Qt library by Trolltech AS, Norway (or with modified versions
 *  of Qt that use the same license as Qt), and distribute linked
 *  combinations including the two.  You must obey the GNU General
 *  Public License in all respects for all of the code used other than
 *  Qt.  If you modify this file, you may extend this exception to
 *  your version of the file, but you are not obligated to do so.  If
 *  you do not wish to do so, delete this exception statement from
 *  your version.
 */
#include "acljobs.h"
#include <kio/scheduler.h>
#include <kdebug.h>

using namespace KMail;

static unsigned int IMAPRightsToPermission( const QString& str ) {
  unsigned int perm = 0;
  bool foundSeenPerm = false;
  uint len = str.length();
  for (uint i = 0; i < len; ++i) {
    QChar ch = str[i];
    switch ( ch.latin1() ) {
    case 'l': perm |= ACLJobs::List; break;
    case 'r': perm |= ACLJobs::Read; break;
    case 's': foundSeenPerm = true; break;
    case 'w': perm |= ACLJobs::WriteFlags; break;
    case 'i': perm |= ACLJobs::Insert; break;
    case 'p': perm |= ACLJobs::Post; break;
    case 'c': perm |= ACLJobs::Create; break;
    case 'd': perm |= ACLJobs::Delete; break;
    case 'a': perm |= ACLJobs::Administer; break;
    default: break;
    }
  }
  if ( ( perm & ACLJobs::Read ) && str.find( 's' ) == -1 ) {
    // Reading without 'seen' is, well, annoying. Unusable, even.
    // So we treat 'rs' as a single one.
    // But if the permissions were set out of kmail, better check that both are set
    kdWarning(5006) << "IMAPRightsToPermission: found read (r) but not seen (s). Things will not work well." << endl;
    if ( perm & ACLJobs::Administer )
      kdWarning(5006) << "You can change this yourself in the ACL dialog" << endl;
    else
      kdWarning(5006) << "Ask your admin for 's' permissions." << endl;
    // Is the above correct enough to be turned into a KMessageBox?
  }

  return perm;
}

static QCString permissionsToIMAPRights( unsigned int permissions ) {
  QCString str = "";
  if ( permissions & ACLJobs::List )
    str += 'l';
  if ( permissions & ACLJobs::Read )
    str += "rs";
  if ( permissions & ACLJobs::WriteFlags )
    str += 'w';
  if ( permissions & ACLJobs::Insert )
    str += 'i';
  if ( permissions & ACLJobs::Post )
    str += 'p';
  if ( permissions & ACLJobs::Create )
    str += 'c';
  if ( permissions & ACLJobs::Delete )
    str += 'd';
  if ( permissions & ACLJobs::Administer )
    str += 'a';
  return str;
}

#ifndef NDEBUG
QString ACLJobs::permissionsToString( unsigned int permissions )
{
  QString str;
  if ( permissions & ACLJobs::List )
    str += "List ";
  if ( permissions & ACLJobs::Read )
    str += "Read ";
  if ( permissions & ACLJobs::WriteFlags )
    str += "Write ";
  if ( permissions & ACLJobs::Insert )
    str += "Insert ";
  if ( permissions & ACLJobs::Post )
    str += "Post ";
  if ( permissions & ACLJobs::Create )
    str += "Create ";
  if ( permissions & ACLJobs::Delete )
    str += "Delete ";
  if ( permissions & ACLJobs::Administer )
    str += "Administer ";
  if ( !str.isEmpty() )
    str.truncate( str.length() - 1 );
  return str;
}
#endif

KIO::SimpleJob* ACLJobs::setACL( KIO::Slave* slave, const KURL& url, const QString& user, unsigned int permissions )
{
  QString perm = QString::fromLatin1( permissionsToIMAPRights( permissions ) );

  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'S' << url << user << perm;

  KIO::SimpleJob* job = KIO::special( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::DeleteACLJob* ACLJobs::deleteACL( KIO::Slave* slave, const KURL& url, const QString& user )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'D' << url << user;

  ACLJobs::DeleteACLJob* job = new ACLJobs::DeleteACLJob( url, user, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetACLJob* ACLJobs::getACL( KIO::Slave* slave, const KURL& url )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'G' << url;

  ACLJobs::GetACLJob* job = new ACLJobs::GetACLJob( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetUserRightsJob* ACLJobs::getUserRights( KIO::Slave* slave, const KURL& url )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'M' << url;

  ACLJobs::GetUserRightsJob* job = new ACLJobs::GetUserRightsJob( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetACLJob::GetACLJob( const KURL& url, const QByteArray &packedArgs,
                                 bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo )
{
  connect( this, SIGNAL(infoMessage(KIO::Job*,const QString&)),
           SLOT(slotInfoMessage(KIO::Job*,const QString&)) );
}

void ACLJobs::GetACLJob::slotInfoMessage( KIO::Job*, const QString& str )
{
  // Parse the result
  QStringList lst = QStringList::split( " ", str );
  while ( lst.count() >= 2 ) // we take items 2 by 2
  {
    QString user = lst.front(); lst.pop_front();
    QString imapRights = lst.front(); lst.pop_front();
    unsigned int perm = IMAPRightsToPermission( imapRights );
    m_entries.append( ACLListEntry( user, imapRights, perm ) );
  }
}

ACLJobs::GetUserRightsJob::GetUserRightsJob( const KURL& url, const QByteArray &packedArgs,
                                               bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo )
{
  connect( this, SIGNAL(infoMessage(KIO::Job*,const QString&)),
           SLOT(slotInfoMessage(KIO::Job*,const QString&)) );
}

void ACLJobs::GetUserRightsJob::slotInfoMessage( KIO::Job*, const QString& str )
{
  // Parse the result
  m_permissions = IMAPRightsToPermission( str );
}

ACLJobs::DeleteACLJob::DeleteACLJob( const KURL& url, const QString& userId,
                                     const QByteArray &packedArgs,
                                     bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo ),
    mUserId( userId )
{
}

////

ACLJobs::MultiSetACLJob::MultiSetACLJob( KIO::Slave* slave, const KURL& url, const ACLList& acl, bool showProgressInfo )
  : KIO::Job( showProgressInfo ),
    mSlave( slave ),
    mUrl( url ), mACLList( acl ), mACLListIterator( acl.begin() )
{
  QTimer::singleShot(0, this, SLOT(slotStart()));
}

void ACLJobs::MultiSetACLJob::slotStart()
{
  // Skip over unchanged entries
  while ( mACLListIterator != mACLList.end() && !(*mACLListIterator).changed )
    ++mACLListIterator;

  if ( mACLListIterator != mACLList.end() )
  {
    const ACLListEntry& entry = *mACLListIterator;
    KIO::Job* job = 0;
    if ( entry.permissions > -1 )
      job = setACL( mSlave, mUrl, entry.userId, entry.permissions );
    else
      job = deleteACL( mSlave, mUrl, entry.userId );

    addSubjob( job );
  } else { // done!
    emitResult();
  }
}

void ACLJobs::MultiSetACLJob::slotResult( KIO::Job *job )
{
  if ( job->error() ) {
    KIO::Job::slotResult( job ); // will set the error and emit result(this)
    return;
  }
  subjobs.remove(job);
  const ACLListEntry& entry = *mACLListIterator;
  emit aclChanged( entry.userId, entry.permissions );

  // Move on to next one
  ++mACLListIterator;
  slotStart();
}

ACLJobs::MultiSetACLJob* ACLJobs::multiSetACL( KIO::Slave* slave, const KURL& url, const ACLList& acl )
{
  return new MultiSetACLJob( slave, url, acl, false /*showProgressInfo*/ );
}

#include "acljobs.moc"
<commit_msg>This looks more sane than relying on the implicit sharing. Found by reading, no bug there.<commit_after>/**
 * acljobs.cpp
 *
 * Copyright (c) 2004 David Faure <faure@kde.org>
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of this program with any edition of
 *  the Qt library by Trolltech AS, Norway (or with modified versions
 *  of Qt that use the same license as Qt), and distribute linked
 *  combinations including the two.  You must obey the GNU General
 *  Public License in all respects for all of the code used other than
 *  Qt.  If you modify this file, you may extend this exception to
 *  your version of the file, but you are not obligated to do so.  If
 *  you do not wish to do so, delete this exception statement from
 *  your version.
 */
#include "acljobs.h"
#include <kio/scheduler.h>
#include <kdebug.h>

using namespace KMail;

static unsigned int IMAPRightsToPermission( const QString& str ) {
  unsigned int perm = 0;
  bool foundSeenPerm = false;
  uint len = str.length();
  for (uint i = 0; i < len; ++i) {
    QChar ch = str[i];
    switch ( ch.latin1() ) {
    case 'l': perm |= ACLJobs::List; break;
    case 'r': perm |= ACLJobs::Read; break;
    case 's': foundSeenPerm = true; break;
    case 'w': perm |= ACLJobs::WriteFlags; break;
    case 'i': perm |= ACLJobs::Insert; break;
    case 'p': perm |= ACLJobs::Post; break;
    case 'c': perm |= ACLJobs::Create; break;
    case 'd': perm |= ACLJobs::Delete; break;
    case 'a': perm |= ACLJobs::Administer; break;
    default: break;
    }
  }
  if ( ( perm & ACLJobs::Read ) && str.find( 's' ) == -1 ) {
    // Reading without 'seen' is, well, annoying. Unusable, even.
    // So we treat 'rs' as a single one.
    // But if the permissions were set out of kmail, better check that both are set
    kdWarning(5006) << "IMAPRightsToPermission: found read (r) but not seen (s). Things will not work well." << endl;
    if ( perm & ACLJobs::Administer )
      kdWarning(5006) << "You can change this yourself in the ACL dialog" << endl;
    else
      kdWarning(5006) << "Ask your admin for 's' permissions." << endl;
    // Is the above correct enough to be turned into a KMessageBox?
  }

  return perm;
}

static QCString permissionsToIMAPRights( unsigned int permissions ) {
  QCString str = "";
  if ( permissions & ACLJobs::List )
    str += 'l';
  if ( permissions & ACLJobs::Read )
    str += "rs";
  if ( permissions & ACLJobs::WriteFlags )
    str += 'w';
  if ( permissions & ACLJobs::Insert )
    str += 'i';
  if ( permissions & ACLJobs::Post )
    str += 'p';
  if ( permissions & ACLJobs::Create )
    str += 'c';
  if ( permissions & ACLJobs::Delete )
    str += 'd';
  if ( permissions & ACLJobs::Administer )
    str += 'a';
  return str;
}

#ifndef NDEBUG
QString ACLJobs::permissionsToString( unsigned int permissions )
{
  QString str;
  if ( permissions & ACLJobs::List )
    str += "List ";
  if ( permissions & ACLJobs::Read )
    str += "Read ";
  if ( permissions & ACLJobs::WriteFlags )
    str += "Write ";
  if ( permissions & ACLJobs::Insert )
    str += "Insert ";
  if ( permissions & ACLJobs::Post )
    str += "Post ";
  if ( permissions & ACLJobs::Create )
    str += "Create ";
  if ( permissions & ACLJobs::Delete )
    str += "Delete ";
  if ( permissions & ACLJobs::Administer )
    str += "Administer ";
  if ( !str.isEmpty() )
    str.truncate( str.length() - 1 );
  return str;
}
#endif

KIO::SimpleJob* ACLJobs::setACL( KIO::Slave* slave, const KURL& url, const QString& user, unsigned int permissions )
{
  QString perm = QString::fromLatin1( permissionsToIMAPRights( permissions ) );

  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'S' << url << user << perm;

  KIO::SimpleJob* job = KIO::special( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::DeleteACLJob* ACLJobs::deleteACL( KIO::Slave* slave, const KURL& url, const QString& user )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'D' << url << user;

  ACLJobs::DeleteACLJob* job = new ACLJobs::DeleteACLJob( url, user, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetACLJob* ACLJobs::getACL( KIO::Slave* slave, const KURL& url )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'G' << url;

  ACLJobs::GetACLJob* job = new ACLJobs::GetACLJob( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetUserRightsJob* ACLJobs::getUserRights( KIO::Slave* slave, const KURL& url )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'M' << url;

  ACLJobs::GetUserRightsJob* job = new ACLJobs::GetUserRightsJob( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetACLJob::GetACLJob( const KURL& url, const QByteArray &packedArgs,
                                 bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo )
{
  connect( this, SIGNAL(infoMessage(KIO::Job*,const QString&)),
           SLOT(slotInfoMessage(KIO::Job*,const QString&)) );
}

void ACLJobs::GetACLJob::slotInfoMessage( KIO::Job*, const QString& str )
{
  // Parse the result
  QStringList lst = QStringList::split( " ", str );
  while ( lst.count() >= 2 ) // we take items 2 by 2
  {
    QString user = lst.front(); lst.pop_front();
    QString imapRights = lst.front(); lst.pop_front();
    unsigned int perm = IMAPRightsToPermission( imapRights );
    m_entries.append( ACLListEntry( user, imapRights, perm ) );
  }
}

ACLJobs::GetUserRightsJob::GetUserRightsJob( const KURL& url, const QByteArray &packedArgs,
                                               bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo )
{
  connect( this, SIGNAL(infoMessage(KIO::Job*,const QString&)),
           SLOT(slotInfoMessage(KIO::Job*,const QString&)) );
}

void ACLJobs::GetUserRightsJob::slotInfoMessage( KIO::Job*, const QString& str )
{
  // Parse the result
  m_permissions = IMAPRightsToPermission( str );
}

ACLJobs::DeleteACLJob::DeleteACLJob( const KURL& url, const QString& userId,
                                     const QByteArray &packedArgs,
                                     bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo ),
    mUserId( userId )
{
}

////

ACLJobs::MultiSetACLJob::MultiSetACLJob( KIO::Slave* slave, const KURL& url, const ACLList& acl, bool showProgressInfo )
  : KIO::Job( showProgressInfo ),
    mSlave( slave ),
    mUrl( url ), mACLList( acl ), mACLListIterator( mACLList.begin() )
{
  QTimer::singleShot(0, this, SLOT(slotStart()));
}

void ACLJobs::MultiSetACLJob::slotStart()
{
  // Skip over unchanged entries
  while ( mACLListIterator != mACLList.end() && !(*mACLListIterator).changed )
    ++mACLListIterator;

  if ( mACLListIterator != mACLList.end() )
  {
    const ACLListEntry& entry = *mACLListIterator;
    KIO::Job* job = 0;
    if ( entry.permissions > -1 )
      job = setACL( mSlave, mUrl, entry.userId, entry.permissions );
    else
      job = deleteACL( mSlave, mUrl, entry.userId );

    addSubjob( job );
  } else { // done!
    emitResult();
  }
}

void ACLJobs::MultiSetACLJob::slotResult( KIO::Job *job )
{
  if ( job->error() ) {
    KIO::Job::slotResult( job ); // will set the error and emit result(this)
    return;
  }
  subjobs.remove(job);
  const ACLListEntry& entry = *mACLListIterator;
  emit aclChanged( entry.userId, entry.permissions );

  // Move on to next one
  ++mACLListIterator;
  slotStart();
}

ACLJobs::MultiSetACLJob* ACLJobs::multiSetACL( KIO::Slave* slave, const KURL& url, const ACLList& acl )
{
  return new MultiSetACLJob( slave, url, acl, false /*showProgressInfo*/ );
}

#include "acljobs.moc"
<|endoftext|>