<commit_before>/*
 * Copyright 2011 Christoph Bumiller
 *           2014 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include "codegen/nv50_ir.h"
#include "codegen/nv50_ir_build_util.h"

#include "codegen/nv50_ir_target_nvc0.h"
#include "codegen/nv50_ir_lowering_gm107.h"

#include <limits>

namespace nv50_ir {

#define QOP_ADD  0
#define QOP_SUBR 1
#define QOP_SUB  2
#define QOP_MOV2 3

//             UL UR LL LR
#define QUADOP(q, r, s, t)                      \
   ((QOP_##q << 6) | (QOP_##r << 4) |           \
    (QOP_##s << 2) | (QOP_##t << 0))

bool
GM107LoweringPass::handleManualTXD(TexInstruction *i)
{
   static const uint8_t qOps[4][2] =
   {
      { QUADOP(MOV2, ADD,  MOV2, ADD),  QUADOP(MOV2, MOV2, ADD,  ADD) }, // l0
      { QUADOP(SUBR, MOV2, SUBR, MOV2), QUADOP(MOV2, MOV2, ADD,  ADD) }, // l1
      { QUADOP(MOV2, ADD,  MOV2, ADD),  QUADOP(SUBR, SUBR, MOV2, MOV2) }, // l2
      { QUADOP(SUBR, MOV2, SUBR, MOV2), QUADOP(SUBR, SUBR, MOV2, MOV2) }, // l3
   };
   Value *def[4][4];
   Value *crd[3];
   Value *tmp;
   Instruction *tex, *add;
   Value *zero = bld.loadImm(bld.getSSA(), 0);
   int l, c;
   const int dim = i->tex.target.getDim();
   const int array = i->tex.target.isArray();

   i->op = OP_TEX; // no need to clone dPdx/dPdy later

   for (c = 0; c < dim; ++c)
      crd[c] = bld.getScratch();
   tmp = bld.getScratch();

   for (l = 0; l < 4; ++l) {
      // mov coordinates from lane l to all lanes
      bld.mkOp(OP_QUADON, TYPE_NONE, NULL);
      for (c = 0; c < dim; ++c) {
         bld.mkOp2(OP_SHFL, TYPE_F32, crd[c], i->getSrc(c + array), bld.mkImm(l));
         add = bld.mkOp2(OP_QUADOP, TYPE_F32, crd[c], crd[c], zero);
         add->subOp = 0x00;
         add->lanes = 1; /* abused for .ndv */
      }

      // add dPdx from lane l to lanes dx
      for (c = 0; c < dim; ++c) {
         bld.mkOp2(OP_SHFL, TYPE_F32, tmp, i->dPdx[c].get(), bld.mkImm(l));
         add = bld.mkOp2(OP_QUADOP, TYPE_F32, crd[c], tmp, crd[c]);
         add->subOp = qOps[l][0];
         add->lanes = 1; /* abused for .ndv */
      }

      // add dPdy from lane l to lanes dy
      for (c = 0; c < dim; ++c) {
         bld.mkOp2(OP_SHFL, TYPE_F32, tmp, i->dPdy[c].get(), bld.mkImm(l));
         add = bld.mkOp2(OP_QUADOP, TYPE_F32, crd[c], tmp, crd[c]);
         add->subOp = qOps[l][1];
         add->lanes = 1; /* abused for .ndv */
      }

      // texture
      bld.insert(tex = cloneForward(func, i));
      for (c = 0; c < dim; ++c)
         tex->setSrc(c + array, crd[c]);
      bld.mkOp(OP_QUADPOP, TYPE_NONE, NULL);

      // save results
      for (c = 0; i->defExists(c); ++c) {
         Instruction *mov;
         def[c][l] = bld.getSSA();
         mov = bld.mkMov(def[c][l], tex->getDef(c));
         mov->fixed = 1;
         mov->lanes = 1 << l;
      }
   }

   for (c = 0; i->defExists(c); ++c) {
      Instruction *u = bld.mkOp(OP_UNION, TYPE_U32, i->getDef(c));
      for (l = 0; l < 4; ++l)
         u->setSrc(l, def[c][l]);
   }

   i->bb->remove(i);
   return true;
}

bool
GM107LoweringPass::handleDFDX(Instruction *insn)
{
   Instruction *shfl;
   int qop = 0, xid = 0;

   switch (insn->op) {
   case OP_DFDX:
      qop = QUADOP(SUB, SUBR, SUB, SUBR);
      xid = 1;
      break;
   case OP_DFDY:
      qop = QUADOP(SUB, SUB, SUBR, SUBR);
      xid = 2;
      break;
   default:
      assert(!"invalid dfdx opcode");
      break;
   }

   shfl = bld.mkOp2(OP_SHFL, TYPE_F32, bld.getScratch(),
                    insn->getSrc(0), bld.mkImm(xid));
   shfl->subOp = NV50_IR_SUBOP_SHFL_BFLY;
   insn->op = OP_QUADOP;
   insn->subOp = qop;
   insn->lanes = 0; /* abused for !.ndv */
   insn->setSrc(1, insn->getSrc(0));
   insn->setSrc(0, shfl->getDef(0));
   return true;
}

bool
GM107LoweringPass::handlePFETCH(Instruction *i)
{
   Value *tmp0 = bld.getScratch();
   Value *tmp1 = bld.getScratch();
   Value *tmp2 = bld.getScratch();
   bld.mkOp1(OP_RDSV, TYPE_U32, tmp0, bld.mkSysVal(SV_INVOCATION_INFO, 0));
   bld.mkOp2(OP_SHR , TYPE_U32, tmp1, tmp0, bld.mkImm(16));
   bld.mkOp2(OP_AND , TYPE_U32, tmp0, tmp0, bld.mkImm(0xff));
   bld.mkOp2(OP_AND , TYPE_U32, tmp1, tmp1, bld.mkImm(0xff));
   bld.mkOp1(OP_MOV , TYPE_U32, tmp2, bld.mkImm(i->getSrc(0)->reg.data.u32));
   bld.mkOp3(OP_MAD , TYPE_U32, tmp0, tmp0, tmp1, tmp2);
   i->setSrc(0, tmp0);
   i->setSrc(1, NULL);
   return true;
}

bool
GM107LoweringPass::handlePOPCNT(Instruction *i)
{
   Value *tmp = bld.mkOp2v(OP_AND, i->sType, bld.getScratch(),
                           i->getSrc(0), i->getSrc(1));
   i->setSrc(0, tmp);
   i->setSrc(1, NULL);
   return TRUE;
}

//
// - add quadop dance for texturing
// - put FP outputs in GPRs
// - convert instruction sequences
//
bool
GM107LoweringPass::visit(Instruction *i)
{
   bld.setPosition(i, false);

   if (i->cc != CC_ALWAYS)
      checkPredicate(i);

   switch (i->op) {
   case OP_TEX:
   case OP_TXB:
   case OP_TXL:
   case OP_TXF:
   case OP_TXG:
      return handleTEX(i->asTex());
   case OP_TXD:
      return handleTXD(i->asTex());
   case OP_TXLQ:
      return handleTXLQ(i->asTex());
   case OP_TXQ:
      return handleTXQ(i->asTex());
   case OP_EX2:
      bld.mkOp1(OP_PREEX2, TYPE_F32, i->getDef(0), i->getSrc(0));
      i->setSrc(0, i->getDef(0));
      break;
   case OP_POW:
      return handlePOW(i);
   case OP_DIV:
      return handleDIV(i);
   case OP_MOD:
      return handleMOD(i);
   case OP_SQRT:
      return handleSQRT(i);
   case OP_EXPORT:
      return handleEXPORT(i);
   case OP_PFETCH:
      return handlePFETCH(i);
   case OP_EMIT:
   case OP_RESTART:
      return handleOUT(i);
   case OP_RDSV:
      return handleRDSV(i);
   case OP_WRSV:
      return handleWRSV(i);
   case OP_LOAD:
      if (i->src(0).getFile() == FILE_SHADER_INPUT) {
         if (prog->getType() == Program::TYPE_COMPUTE) {
            i->getSrc(0)->reg.file = FILE_MEMORY_CONST;
            i->getSrc(0)->reg.fileIndex = 0;
         } else
         if (prog->getType() == Program::TYPE_GEOMETRY &&
             i->src(0).isIndirect(0)) {
            // XXX: this assumes vec4 units
            Value *ptr = bld.mkOp2v(OP_SHL, TYPE_U32, bld.getSSA(),
                                    i->getIndirect(0, 0), bld.mkImm(4));
            i->setIndirect(0, 0, ptr);
         } else {
            i->op = OP_VFETCH;
            assert(prog->getType() != Program::TYPE_FRAGMENT); // INTERP
         }
      }
      break;
   case OP_ATOM:
   {
      const bool cctl = i->src(0).getFile() == FILE_MEMORY_GLOBAL;
      handleATOM(i);
      handleCasExch(i, cctl);
   }
      break;
   case OP_SULDB:
   case OP_SULDP:
   case OP_SUSTB:
   case OP_SUSTP:
   case OP_SUREDB:
   case OP_SUREDP:
      handleSurfaceOpNVE4(i->asTex());
      break;
   case OP_DFDX:
   case OP_DFDY:
      handleDFDX(i);
      break;
   case OP_POPCNT:
      handlePOPCNT(i);
      break;
   default:
      break;
   }
   return true;
}

} // namespace nv50_ir
<commit_msg>gm107/ir: add support for indirect const buffer selection<commit_after>/*
 * Copyright 2011 Christoph Bumiller
 *           2014 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include "codegen/nv50_ir.h"
#include "codegen/nv50_ir_build_util.h"

#include "codegen/nv50_ir_target_nvc0.h"
#include "codegen/nv50_ir_lowering_gm107.h"

#include <limits>

namespace nv50_ir {

#define QOP_ADD  0
#define QOP_SUBR 1
#define QOP_SUB  2
#define QOP_MOV2 3

//             UL UR LL LR
#define QUADOP(q, r, s, t)                      \
   ((QOP_##q << 6) | (QOP_##r << 4) |           \
    (QOP_##s << 2) | (QOP_##t << 0))

bool
GM107LoweringPass::handleManualTXD(TexInstruction *i)
{
   static const uint8_t qOps[4][2] =
   {
      { QUADOP(MOV2, ADD,  MOV2, ADD),  QUADOP(MOV2, MOV2, ADD,  ADD) }, // l0
      { QUADOP(SUBR, MOV2, SUBR, MOV2), QUADOP(MOV2, MOV2, ADD,  ADD) }, // l1
      { QUADOP(MOV2, ADD,  MOV2, ADD),  QUADOP(SUBR, SUBR, MOV2, MOV2) }, // l2
      { QUADOP(SUBR, MOV2, SUBR, MOV2), QUADOP(SUBR, SUBR, MOV2, MOV2) }, // l3
   };
   Value *def[4][4];
   Value *crd[3];
   Value *tmp;
   Instruction *tex, *add;
   Value *zero = bld.loadImm(bld.getSSA(), 0);
   int l, c;
   const int dim = i->tex.target.getDim();
   const int array = i->tex.target.isArray();

   i->op = OP_TEX; // no need to clone dPdx/dPdy later

   for (c = 0; c < dim; ++c)
      crd[c] = bld.getScratch();
   tmp = bld.getScratch();

   for (l = 0; l < 4; ++l) {
      // mov coordinates from lane l to all lanes
      bld.mkOp(OP_QUADON, TYPE_NONE, NULL);
      for (c = 0; c < dim; ++c) {
         bld.mkOp2(OP_SHFL, TYPE_F32, crd[c], i->getSrc(c + array), bld.mkImm(l));
         add = bld.mkOp2(OP_QUADOP, TYPE_F32, crd[c], crd[c], zero);
         add->subOp = 0x00;
         add->lanes = 1; /* abused for .ndv */
      }

      // add dPdx from lane l to lanes dx
      for (c = 0; c < dim; ++c) {
         bld.mkOp2(OP_SHFL, TYPE_F32, tmp, i->dPdx[c].get(), bld.mkImm(l));
         add = bld.mkOp2(OP_QUADOP, TYPE_F32, crd[c], tmp, crd[c]);
         add->subOp = qOps[l][0];
         add->lanes = 1; /* abused for .ndv */
      }

      // add dPdy from lane l to lanes dy
      for (c = 0; c < dim; ++c) {
         bld.mkOp2(OP_SHFL, TYPE_F32, tmp, i->dPdy[c].get(), bld.mkImm(l));
         add = bld.mkOp2(OP_QUADOP, TYPE_F32, crd[c], tmp, crd[c]);
         add->subOp = qOps[l][1];
         add->lanes = 1; /* abused for .ndv */
      }

      // texture
      bld.insert(tex = cloneForward(func, i));
      for (c = 0; c < dim; ++c)
         tex->setSrc(c + array, crd[c]);
      bld.mkOp(OP_QUADPOP, TYPE_NONE, NULL);

      // save results
      for (c = 0; i->defExists(c); ++c) {
         Instruction *mov;
         def[c][l] = bld.getSSA();
         mov = bld.mkMov(def[c][l], tex->getDef(c));
         mov->fixed = 1;
         mov->lanes = 1 << l;
      }
   }

   for (c = 0; i->defExists(c); ++c) {
      Instruction *u = bld.mkOp(OP_UNION, TYPE_U32, i->getDef(c));
      for (l = 0; l < 4; ++l)
         u->setSrc(l, def[c][l]);
   }

   i->bb->remove(i);
   return true;
}

bool
GM107LoweringPass::handleDFDX(Instruction *insn)
{
   Instruction *shfl;
   int qop = 0, xid = 0;

   switch (insn->op) {
   case OP_DFDX:
      qop = QUADOP(SUB, SUBR, SUB, SUBR);
      xid = 1;
      break;
   case OP_DFDY:
      qop = QUADOP(SUB, SUB, SUBR, SUBR);
      xid = 2;
      break;
   default:
      assert(!"invalid dfdx opcode");
      break;
   }

   shfl = bld.mkOp2(OP_SHFL, TYPE_F32, bld.getScratch(),
                    insn->getSrc(0), bld.mkImm(xid));
   shfl->subOp = NV50_IR_SUBOP_SHFL_BFLY;
   insn->op = OP_QUADOP;
   insn->subOp = qop;
   insn->lanes = 0; /* abused for !.ndv */
   insn->setSrc(1, insn->getSrc(0));
   insn->setSrc(0, shfl->getDef(0));
   return true;
}

bool
GM107LoweringPass::handlePFETCH(Instruction *i)
{
   Value *tmp0 = bld.getScratch();
   Value *tmp1 = bld.getScratch();
   Value *tmp2 = bld.getScratch();
   bld.mkOp1(OP_RDSV, TYPE_U32, tmp0, bld.mkSysVal(SV_INVOCATION_INFO, 0));
   bld.mkOp2(OP_SHR , TYPE_U32, tmp1, tmp0, bld.mkImm(16));
   bld.mkOp2(OP_AND , TYPE_U32, tmp0, tmp0, bld.mkImm(0xff));
   bld.mkOp2(OP_AND , TYPE_U32, tmp1, tmp1, bld.mkImm(0xff));
   bld.mkOp1(OP_MOV , TYPE_U32, tmp2, bld.mkImm(i->getSrc(0)->reg.data.u32));
   bld.mkOp3(OP_MAD , TYPE_U32, tmp0, tmp0, tmp1, tmp2);
   i->setSrc(0, tmp0);
   i->setSrc(1, NULL);
   return true;
}

bool
GM107LoweringPass::handlePOPCNT(Instruction *i)
{
   Value *tmp = bld.mkOp2v(OP_AND, i->sType, bld.getScratch(),
                           i->getSrc(0), i->getSrc(1));
   i->setSrc(0, tmp);
   i->setSrc(1, NULL);
   return TRUE;
}

//
// - add quadop dance for texturing
// - put FP outputs in GPRs
// - convert instruction sequences
//
bool
GM107LoweringPass::visit(Instruction *i)
{
   bld.setPosition(i, false);

   if (i->cc != CC_ALWAYS)
      checkPredicate(i);

   switch (i->op) {
   case OP_TEX:
   case OP_TXB:
   case OP_TXL:
   case OP_TXF:
   case OP_TXG:
      return handleTEX(i->asTex());
   case OP_TXD:
      return handleTXD(i->asTex());
   case OP_TXLQ:
      return handleTXLQ(i->asTex());
   case OP_TXQ:
      return handleTXQ(i->asTex());
   case OP_EX2:
      bld.mkOp1(OP_PREEX2, TYPE_F32, i->getDef(0), i->getSrc(0));
      i->setSrc(0, i->getDef(0));
      break;
   case OP_POW:
      return handlePOW(i);
   case OP_DIV:
      return handleDIV(i);
   case OP_MOD:
      return handleMOD(i);
   case OP_SQRT:
      return handleSQRT(i);
   case OP_EXPORT:
      return handleEXPORT(i);
   case OP_PFETCH:
      return handlePFETCH(i);
   case OP_EMIT:
   case OP_RESTART:
      return handleOUT(i);
   case OP_RDSV:
      return handleRDSV(i);
   case OP_WRSV:
      return handleWRSV(i);
   case OP_LOAD:
      if (i->src(0).getFile() == FILE_SHADER_INPUT) {
         if (prog->getType() == Program::TYPE_COMPUTE) {
            i->getSrc(0)->reg.file = FILE_MEMORY_CONST;
            i->getSrc(0)->reg.fileIndex = 0;
         } else
         if (prog->getType() == Program::TYPE_GEOMETRY &&
             i->src(0).isIndirect(0)) {
            // XXX: this assumes vec4 units
            Value *ptr = bld.mkOp2v(OP_SHL, TYPE_U32, bld.getSSA(),
                                    i->getIndirect(0, 0), bld.mkImm(4));
            i->setIndirect(0, 0, ptr);
         } else {
            i->op = OP_VFETCH;
            assert(prog->getType() != Program::TYPE_FRAGMENT); // INTERP
         }
      } else if (i->src(0).getFile() == FILE_MEMORY_CONST) {
         if (i->src(0).isIndirect(1)) {
            Value *ptr;
            if (i->src(0).isIndirect(0))
               ptr = bld.mkOp3v(OP_INSBF, TYPE_U32, bld.getSSA(),
                                i->getIndirect(0, 1), bld.mkImm(0x1010),
                                i->getIndirect(0, 0));
            else
               ptr = bld.mkOp2v(OP_SHL, TYPE_U32, bld.getSSA(),
                                i->getIndirect(0, 1), bld.mkImm(16));
            i->setIndirect(0, 1, NULL);
            i->setIndirect(0, 0, ptr);
            i->subOp = NV50_IR_SUBOP_LDC_IS;
         }
      }
      break;
   case OP_ATOM:
   {
      const bool cctl = i->src(0).getFile() == FILE_MEMORY_GLOBAL;
      handleATOM(i);
      handleCasExch(i, cctl);
   }
      break;
   case OP_SULDB:
   case OP_SULDP:
   case OP_SUSTB:
   case OP_SUSTP:
   case OP_SUREDB:
   case OP_SUREDP:
      handleSurfaceOpNVE4(i->asTex());
      break;
   case OP_DFDX:
   case OP_DFDY:
      handleDFDX(i);
      break;
   case OP_POPCNT:
      handlePOPCNT(i);
      break;
   default:
      break;
   }
   return true;
}

} // namespace nv50_ir
<|endoftext|>


<commit_before>#include "src/fillers.h"
#include "src/utils.h"

/* ***** */
void gfx_flatFill(const gfx_Triangle *t, gfx_drawBuffer *buffer, enum TriangleType type)
{
    const gfx_Vertex *v0 = &t->vertices[0];
    const gfx_Vertex *v1 = &t->vertices[1];
    const gfx_Vertex *v2 = &t->vertices[2];
    double y, invDy, dxLeft, dxRight, xLeft, xRight, prestep;
    int currLine, numScanlines, x0, x1, yDir = 1;
    // variables used if depth test is enabled
    float startInvZ, endInvZ, invZ0, invZ1, invZ2, invY02;

    if(type == FLAT_BOTTOM)
    {
        invDy  = 1.0 / (v2->position.y - v0->position.y);
        numScanlines = ceil(v2->position.y) - ceil(v0->position.y);
        prestep = ceil(v0->position.y) - v0->position.y;
        // todo: handle line sizes of height < 1
        if(v2->position.y - v0->position.y < 1) return;
    }
    else
    {
        invDy  = 1.0 / (v0->position.y - v2->position.y);
        yDir = -1;
        numScanlines = ceil(v0->position.y) - ceil(v2->position.y);
        prestep = ceil(v2->position.y) - v2->position.y;
        // todo: handle line sizes of height < 1
        if(v0->position.y - v2->position.y < 1) return;
    }

    dxLeft  = (v2->position.x - v0->position.x) * invDy;
    dxRight = (v1->position.x - v0->position.x) * invDy;
    xLeft   = v0->position.x + dxLeft * prestep;
    xRight  = v0->position.x + dxRight * prestep;

    // skip the unnecessary divisions if there's no depth testing
    if(buffer->drawOpts.depthFunc != DF_ALWAYS)
    {
        invZ0  = 1.f / v0->position.z;
        invZ1  = 1.f / v1->position.z;
        invZ2  = 1.f / v2->position.z;
        invY02 = 1.f / (v0->position.y - v2->position.y);
    }

    for(currLine = 0, y = ceil(v0->position.y); currLine <= numScanlines; y += yDir)
    {
        x0 = ceil(xLeft);
        x1 = ceil(xRight);

        // interpolate 1/z only if depth testing is enabled
        if(buffer->drawOpts.depthFunc != DF_ALWAYS)
        {
            float r1  = (v0->position.y - y) * invY02;
            startInvZ = LERP(invZ0, invZ2, r1);
            endInvZ   = LERP(invZ0, invZ1, r1);
            gfx_drawLine(x0, y, 1.f/startInvZ, x1, y, 1.f/endInvZ, t->color, buffer);
        }
        else
            gfx_drawLine(x0, y, 0.f, x1, y, 0.f, t->color, buffer);

        if(++currLine < numScanlines)
        {
            xLeft  += dxLeft;
            xRight += dxRight;
        }
    }
}

/* ***** */
void gfx_perspectiveTextureMap(const gfx_Triangle *t, gfx_drawBuffer *buffer, enum TriangleType type)
{
    const gfx_Vertex *v0 = &t->vertices[0];
    const gfx_Vertex *v1 = &t->vertices[1];
    const gfx_Vertex *v2 = &t->vertices[2];
    double x, y, invDy, dxLeft, dxRight, prestep, yDir = 1;
    double startX, endX, startXPrestep, endXPrestep;
    int   useColorKey = buffer->drawOpts.colorKey != -1 ? 1 : 0;
    int   texW = t->texture->width - 1;
    int   texH = t->texture->height - 1;
    int   texArea = texW * texH;
    int   currLine, numScanlines;
    float invZ0, invZ1, invZ2, invY02 = 1.f;

    if(type == FLAT_BOTTOM)
    {
        invDy  = 1.0 / (v2->position.y - v0->position.y);
        numScanlines = ceil(v2->position.y) - ceil(v0->position.y);
        prestep = ceil(v0->position.y) - v0->position.y;
        // todo: handle line sizes of height < 1
        if(v2->position.y - v0->position.y < 1) return;
    }
    else
    {
        invDy  = 1.0 / (v0->position.y - v2->position.y);
        yDir = -1;
        numScanlines = ceil(v0->position.y) - ceil(v2->position.y);
        prestep = ceil(v2->position.y) - v2->position.y;
        // todo: handle line sizes of height < 1
        if(v0->position.y - v2->position.y < 1) return;
    }

    dxLeft  = (v2->position.x - v0->position.x) * invDy;
    dxRight = (v1->position.x - v0->position.x) * invDy;
    startX  = v0->position.x;
    endX    = startX;
    startXPrestep = v0->position.x + dxLeft * prestep;
    endXPrestep   = v0->position.x + dxRight * prestep;

    invZ0  = 1.f / v0->position.z;
    invZ1  = 1.f / v1->position.z;
    invZ2  = 1.f / v2->position.z;
    invY02 = 1.f / (v0->position.y - v2->position.y);

    for(currLine = 0, y = v0->position.y; currLine <= numScanlines; y += yDir)
    {
        float startInvZ, endInvZ, r1, invLineLength = 0.f;
        float startU = texW, startV = texH, endU = texW, endV = texH;

        r1 = (v0->position.y - y) * invY02;
        startInvZ = LERP(invZ0, invZ2, r1);
        endInvZ   = LERP(invZ0, invZ1, r1);

        startU *= LERP(v0->uv.u * invZ0, v2->uv.u * invZ2, r1);
        startV *= LERP(v0->uv.v * invZ0, v2->uv.v * invZ2, r1);
        endU   *= LERP(v0->uv.u * invZ0, v1->uv.u * invZ1, r1);
        endV   *= LERP(v0->uv.v * invZ0, v1->uv.v * invZ1, r1);

        if(startX != endX)
            invLineLength = 1.f / (endX - startX);

        for(x = startXPrestep; x <= endXPrestep; ++x)
        {
            // interpolate 1/z for each pixel in the scanline
            float r = (x - startX) * invLineLength;
            float lerpInvZ = LERP(startInvZ, endInvZ, r);
            float z = 1.f/lerpInvZ;
            float u = z * LERP(startU, endU, r);
            float v = z * LERP(startV, endV, r);

            // fetch texture data with a texArea modulus for proper effect in case u or v are > 1
            unsigned char pixel = t->texture->data[((int)u + ((int)v * t->texture->height)) % texArea];

            if(!useColorKey || (useColorKey && pixel != (unsigned char)buffer->drawOpts.colorKey))
            {
                // DF_ALWAYS = no depth test
                if(buffer->drawOpts.depthFunc == DF_ALWAYS)
                    gfx_drawPixel(ceil(x), ceil(y), pixel, buffer);
                else
                    gfx_drawPixelWithDepth(ceil(x), ceil(y), lerpInvZ, pixel, buffer);
            }
        }

        if(++currLine < numScanlines)
        {
            startX += dxLeft;
            endX   += dxRight;
            startXPrestep += dxLeft;
            endXPrestep   += dxRight;
        }
    }
}

/* ***** */
void gfx_affineTextureMap(const gfx_Triangle *t, gfx_drawBuffer *buffer, enum TriangleType type)
{
    const gfx_Vertex *v0 = &t->vertices[0];
    const gfx_Vertex *v1 = &t->vertices[1];
    const gfx_Vertex *v2 = &t->vertices[2];
    double x, y, invDy, dxLeft, dxRight, prestep, yDir = 1;
    double startU, startV, invDx, du, dv, startX, endX;
    float duLeft, dvLeft, duRight, dvRight;
    float texW = t->texture->width - 1;
    float texH = t->texture->height - 1;
    int   useColorKey = buffer->drawOpts.colorKey != -1 ? 1 : 0;
    int   texArea = texW * texH;
    int   currLine, numScanlines;
    // variables used only if depth test is enabled
    float invZ0, invZ1, invZ2, invY02 = 1.f;

    if(type == FLAT_BOTTOM)
    {
        invDy  = 1.f / (v2->position.y - v0->position.y);
        numScanlines = ceil(v2->position.y) - ceil(v0->position.y);
        prestep = ceil(v0->position.y) - v0->position.y;
        // todo: handle line sizes of height < 1
        if(v2->position.y - v0->position.y < 1) return;
    }
    else
    {
        invDy  = 1.f / (v0->position.y - v2->position.y);
        yDir = -1;
        numScanlines = ceil(v0->position.y) - ceil(v2->position.y);
        prestep = ceil(v2->position.y) - v2->position.y;
        // todo: handle line sizes of height < 1
        if(v0->position.y - v2->position.y < 1) return;
    }

    dxLeft  = (v2->position.x - v0->position.x) * invDy;
    dxRight = (v1->position.x - v0->position.x) * invDy;

    duLeft  = texW * (v2->uv.u - v0->uv.u) * invDy;
    dvLeft  = texH * (v2->uv.v - v0->uv.v) * invDy;
    duRight = texW * (v1->uv.u - v0->uv.u) * invDy;
    dvRight = texH * (v1->uv.v - v0->uv.v) * invDy;

    startU = texW * v0->uv.u + duLeft * prestep;
    startV = texH * v0->uv.v + dvLeft * prestep;
    // With triangles the texture gradients (u,v slopes over the triangle surface)
    // are guaranteed to be constant, so we need to calculate du and dv only once.
    invDx = 1.f / (dxRight - dxLeft);
    du = (duRight - duLeft) * invDx;
    dv = (dvRight - dvLeft) * invDx;
    startX = v0->position.x + dxLeft * prestep;
    endX   = v0->position.x + dxRight * prestep;

    // skip the unnecessary divisions if there's no depth testing
    if(buffer->drawOpts.depthFunc != DF_ALWAYS)
    {
        invZ0  = 1.f / v0->position.z;
        invZ1  = 1.f / v1->position.z;
        invZ2  = 1.f / v2->position.z;
        invY02 = 1.f / (v0->position.y - v2->position.y);
    }

    for(currLine = 0, y = v0->position.y; currLine <= numScanlines; y += yDir)
    {
        float u = startU;
        float v = startV;
        // variables used only if depth test is enabled
        float startInvZ, endInvZ, invLineLength = 0.f;

        // interpolate 1/z only if depth testing is enabled
        if(buffer->drawOpts.depthFunc != DF_ALWAYS)
        {
            float r1  = (v0->position.y - y) * invY02;
            startInvZ = LERP(invZ0, invZ2, r1);
            endInvZ   = LERP(invZ0, invZ1, r1);

            if(startX != endX)
                invLineLength = 1.f / (endX - startX);
        }

        for(x = startX; x <= endX; ++x)
        {
            // fetch texture data with a texArea modulus for proper effect in case u or v are > 1
            unsigned char pixel = t->texture->data[((int)u + ((int)v * t->texture->height)) % texArea];

            if(!useColorKey || (useColorKey && pixel != (unsigned char)buffer->drawOpts.colorKey))
            {
                // DF_ALWAYS = no depth test
                if(buffer->drawOpts.depthFunc == DF_ALWAYS)
                    gfx_drawPixel(ceil(x), ceil(y), pixel, buffer);
                else
                {
                    float r = (x - startX) * invLineLength;
                    float lerpInvZ = LERP(startInvZ, endInvZ, r);
                    gfx_drawPixelWithDepth(ceil(x), ceil(y), lerpInvZ, pixel, buffer);
                }
            }
            u += du;
            v += dv;
        }

        if(++currLine < numScanlines)
        {
            startX += dxLeft;
            endX   += dxRight;
            startU += duLeft;
            startV += dvLeft;
        }
    }
}
<commit_msg>fixed single scanline depth buffer glitch<commit_after>#include "src/fillers.h"
#include "src/utils.h"

/* ***** */
void gfx_flatFill(const gfx_Triangle *t, gfx_drawBuffer *buffer, enum TriangleType type)
{
    const gfx_Vertex *v0 = &t->vertices[0];
    const gfx_Vertex *v1 = &t->vertices[1];
    const gfx_Vertex *v2 = &t->vertices[2];
    double y, invDy, dxLeft, dxRight, xLeft, xRight, prestep;
    int currLine, numScanlines, x0, x1, yDir = 1;
    // variables used if depth test is enabled
    float startInvZ, endInvZ, invZ0, invZ1, invZ2, invY02;

    if(type == FLAT_BOTTOM)
    {
        invDy  = 1.0 / (v2->position.y - v0->position.y);
        numScanlines = ceil(v2->position.y) - ceil(v0->position.y);
        prestep = ceil(v0->position.y) - v0->position.y;
        // todo: handle line sizes of height < 1
        if(v2->position.y - v0->position.y < 1) return;
    }
    else
    {
        invDy  = 1.0 / (v0->position.y - v2->position.y);
        yDir = -1;
        numScanlines = ceil(v0->position.y) - ceil(v2->position.y);
        prestep = ceil(v2->position.y) - v2->position.y;
        // todo: handle line sizes of height < 1
        if(v0->position.y - v2->position.y < 1) return;
    }

    dxLeft  = (v2->position.x - v0->position.x) * invDy;
    dxRight = (v1->position.x - v0->position.x) * invDy;
    xLeft   = v0->position.x + dxLeft * prestep;
    xRight  = v0->position.x + dxRight * prestep;

    // skip the unnecessary divisions if there's no depth testing
    if(buffer->drawOpts.depthFunc != DF_ALWAYS)
    {
        invZ0  = 1.f / v0->position.z;
        invZ1  = 1.f / v1->position.z;
        invZ2  = 1.f / v2->position.z;
        invY02 = 1.f / (v0->position.y - v2->position.y);
    }

    for(currLine = 0, y = ceil(v0->position.y); currLine <= numScanlines; y += yDir)
    {
        x0 = ceil(xLeft);
        x1 = ceil(xRight);

        // interpolate 1/z only if depth testing is enabled
        if(buffer->drawOpts.depthFunc != DF_ALWAYS)
        {
            float r1  = (v0->position.y - y) * invY02;
            startInvZ = LERP(invZ0, invZ2, r1);
            endInvZ   = LERP(invZ0, invZ1, r1);
            gfx_drawLine(x0, y, 1.f/startInvZ, x1, y, 1.f/endInvZ, t->color, buffer);
        }
        else
            gfx_drawLine(x0, y, 0.f, x1, y, 0.f, t->color, buffer);

        if(++currLine < numScanlines)
        {
            xLeft  += dxLeft;
            xRight += dxRight;
        }
    }
}

/* ***** */
void gfx_perspectiveTextureMap(const gfx_Triangle *t, gfx_drawBuffer *buffer, enum TriangleType type)
{
    const gfx_Vertex *v0 = &t->vertices[0];
    const gfx_Vertex *v1 = &t->vertices[1];
    const gfx_Vertex *v2 = &t->vertices[2];
    double x, y, invDy, dxLeft, dxRight, prestep, yDir = 1;
    double startX, endX, startXPrestep, endXPrestep;
    int   useColorKey = buffer->drawOpts.colorKey != -1 ? 1 : 0;
    int   texW = t->texture->width - 1;
    int   texH = t->texture->height - 1;
    int   texArea = texW * texH;
    int   currLine, numScanlines;
    float invZ0, invZ1, invZ2, invY02 = 1.f;

    if(type == FLAT_BOTTOM)
    {
        invDy  = 1.0 / (v2->position.y - v0->position.y);
        numScanlines = ceil(v2->position.y) - ceil(v0->position.y);
        prestep = ceil(v0->position.y) - v0->position.y;
        // todo: handle line sizes of height < 1
        if(v2->position.y - v0->position.y < 1) return;
    }
    else
    {
        invDy  = 1.0 / (v0->position.y - v2->position.y);
        yDir = -1;
        numScanlines = ceil(v0->position.y) - ceil(v2->position.y);
        prestep = ceil(v2->position.y) - v2->position.y;
        // todo: handle line sizes of height < 1
        if(v0->position.y - v2->position.y < 1) return;
    }

    dxLeft  = (v2->position.x - v0->position.x) * invDy;
    dxRight = (v1->position.x - v0->position.x) * invDy;
    startX  = v0->position.x;
    endX    = startX;
    startXPrestep = v0->position.x + dxLeft * prestep;
    endXPrestep   = v0->position.x + dxRight * prestep;

    invZ0  = 1.f / v0->position.z;
    invZ1  = 1.f / v1->position.z;
    invZ2  = 1.f / v2->position.z;
    invY02 = 1.f / (v0->position.y - v2->position.y);

    for(currLine = 0, y = v0->position.y; currLine <= numScanlines; y += yDir)
    {
        float startInvZ, endInvZ, r1, invLineLength = 0.f;
        float startU = texW, startV = texH, endU = texW, endV = texH;

        r1 = (v0->position.y - y) * invY02;
        startInvZ = LERP(invZ0, invZ2, r1);
        endInvZ   = LERP(invZ0, invZ1, r1);

        startU *= LERP(v0->uv.u * invZ0, v2->uv.u * invZ2, r1);
        startV *= LERP(v0->uv.v * invZ0, v2->uv.v * invZ2, r1);
        endU   *= LERP(v0->uv.u * invZ0, v1->uv.u * invZ1, r1);
        endV   *= LERP(v0->uv.v * invZ0, v1->uv.v * invZ1, r1);

        if(startX != endX)
            invLineLength = 1.f / (endX - startX);

        for(x = startXPrestep; x <= endXPrestep; ++x)
        {
            // interpolate 1/z for each pixel in the scanline
            float r = (x - startX) * invLineLength;
            float lerpInvZ = LERP(startInvZ, endInvZ, r);
            float z = 1.f/lerpInvZ;
            float u = z * LERP(startU, endU, r);
            float v = z * LERP(startV, endV, r);

            // fetch texture data with a texArea modulus for proper effect in case u or v are > 1
            unsigned char pixel = t->texture->data[((int)u + ((int)v * t->texture->height)) % texArea];

            if(!useColorKey || (useColorKey && pixel != (unsigned char)buffer->drawOpts.colorKey))
            {
                // DF_ALWAYS = no depth test
                if(buffer->drawOpts.depthFunc == DF_ALWAYS)
                    gfx_drawPixel(ceil(x), ceil(y), pixel, buffer);
                else
                    gfx_drawPixelWithDepth(ceil(x), ceil(y), lerpInvZ, pixel, buffer);
            }
        }

        startX += dxLeft;
        endX   += dxRight;

        if(++currLine < numScanlines)
        {
            startXPrestep += dxLeft;
            endXPrestep   += dxRight;
        }
    }
}

/* ***** */
void gfx_affineTextureMap(const gfx_Triangle *t, gfx_drawBuffer *buffer, enum TriangleType type)
{
    const gfx_Vertex *v0 = &t->vertices[0];
    const gfx_Vertex *v1 = &t->vertices[1];
    const gfx_Vertex *v2 = &t->vertices[2];
    double x, y, invDy, dxLeft, dxRight, prestep, yDir = 1;
    double startU, startV, invDx, du, dv, startX, endX;
    float duLeft, dvLeft, duRight, dvRight;
    float texW = t->texture->width - 1;
    float texH = t->texture->height - 1;
    int   useColorKey = buffer->drawOpts.colorKey != -1 ? 1 : 0;
    int   texArea = texW * texH;
    int   currLine, numScanlines;
    // variables used only if depth test is enabled
    float invZ0, invZ1, invZ2, invY02 = 1.f;

    if(type == FLAT_BOTTOM)
    {
        invDy  = 1.f / (v2->position.y - v0->position.y);
        numScanlines = ceil(v2->position.y) - ceil(v0->position.y);
        prestep = ceil(v0->position.y) - v0->position.y;
        // todo: handle line sizes of height < 1
        if(v2->position.y - v0->position.y < 1) return;
    }
    else
    {
        invDy  = 1.f / (v0->position.y - v2->position.y);
        yDir = -1;
        numScanlines = ceil(v0->position.y) - ceil(v2->position.y);
        prestep = ceil(v2->position.y) - v2->position.y;
        // todo: handle line sizes of height < 1
        if(v0->position.y - v2->position.y < 1) return;
    }

    dxLeft  = (v2->position.x - v0->position.x) * invDy;
    dxRight = (v1->position.x - v0->position.x) * invDy;

    duLeft  = texW * (v2->uv.u - v0->uv.u) * invDy;
    dvLeft  = texH * (v2->uv.v - v0->uv.v) * invDy;
    duRight = texW * (v1->uv.u - v0->uv.u) * invDy;
    dvRight = texH * (v1->uv.v - v0->uv.v) * invDy;

    startU = texW * v0->uv.u + duLeft * prestep;
    startV = texH * v0->uv.v + dvLeft * prestep;
    // With triangles the texture gradients (u,v slopes over the triangle surface)
    // are guaranteed to be constant, so we need to calculate du and dv only once.
    invDx = 1.f / (dxRight - dxLeft);
    du = (duRight - duLeft) * invDx;
    dv = (dvRight - dvLeft) * invDx;
    startX = v0->position.x + dxLeft * prestep;
    endX   = v0->position.x + dxRight * prestep;

    // skip the unnecessary divisions if there's no depth testing
    if(buffer->drawOpts.depthFunc != DF_ALWAYS)
    {
        invZ0  = 1.f / v0->position.z;
        invZ1  = 1.f / v1->position.z;
        invZ2  = 1.f / v2->position.z;
        invY02 = 1.f / (v0->position.y - v2->position.y);
    }

    for(currLine = 0, y = v0->position.y; currLine <= numScanlines; y += yDir)
    {
        float u = startU;
        float v = startV;
        // variables used only if depth test is enabled
        float startInvZ, endInvZ, invLineLength = 0.f;

        // interpolate 1/z only if depth testing is enabled
        if(buffer->drawOpts.depthFunc != DF_ALWAYS)
        {
            float r1  = (v0->position.y - y) * invY02;
            startInvZ = LERP(invZ0, invZ2, r1);
            endInvZ   = LERP(invZ0, invZ1, r1);

            if(startX != endX)
                invLineLength = 1.f / (endX - startX);
        }

        for(x = startX; x <= endX; ++x)
        {
            // fetch texture data with a texArea modulus for proper effect in case u or v are > 1
            unsigned char pixel = t->texture->data[((int)u + ((int)v * t->texture->height)) % texArea];

            if(!useColorKey || (useColorKey && pixel != (unsigned char)buffer->drawOpts.colorKey))
            {
                // DF_ALWAYS = no depth test
                if(buffer->drawOpts.depthFunc == DF_ALWAYS)
                    gfx_drawPixel(ceil(x), ceil(y), pixel, buffer);
                else
                {
                    float r = (x - startX) * invLineLength;
                    float lerpInvZ = LERP(startInvZ, endInvZ, r);
                    gfx_drawPixelWithDepth(ceil(x), ceil(y), lerpInvZ, pixel, buffer);
                }
            }
            u += du;
            v += dv;
        }

        if(++currLine < numScanlines)
        {
            startX += dxLeft;
            endX   += dxRight;
            startU += duLeft;
            startV += dvLeft;
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Intermodalics All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "tango_ros_native/occupancy_grid_file_io.h"

#include <cmath>
#include <fstream>
#include <iostream>

#include <geometry_msgs/Quaternion.h>
#include <tf/LinearMath/Matrix3x3.h>

#include <glog/logging.h>

#include <yaml-cpp/yaml.h>

namespace {
void AddTrailingSlashToDirectoryPathIfNeeded(std::string& directory_path) {
  if (!directory_path.empty() && directory_path.back() != '/') {
    directory_path += "/";
  }
}
} // namespace

namespace occupancy_grid_file_io {
bool SaveOccupancyGridToFiles(
    const std::string& map_name, const std::string& map_uuid,
    const std::string& map_directory, const nav_msgs::OccupancyGrid& occupancy_grid) {
  return SaveOccupancyGridDataToPgmFile(map_name, map_directory, occupancy_grid)
      && SaveOccupancyGridMetadataToYamlFile(map_name, map_uuid, map_directory, occupancy_grid.info);
}

bool SaveOccupancyGridDataToPgmFile(
    const std::string& map_name, const std::string& map_directory,
    const nav_msgs::OccupancyGrid& occupancy_grid) {
  std::string map_directory_with_trailing_slash = map_directory;
  AddTrailingSlashToDirectoryPathIfNeeded(map_directory_with_trailing_slash);

  std::string map_pgm_file_path = map_directory_with_trailing_slash + map_name + ".pgm";
  FILE* pgm_file = fopen(map_pgm_file_path.c_str(), "w");
  if (!pgm_file) {
    LOG(ERROR) << "Could no open file " << map_pgm_file_path;
    return false;
  }

  fprintf(pgm_file, "P5\n# CREATOR: TangoRosStreamer %.3f m/pix\n%d %d\n255\n",
          occupancy_grid.info.resolution,
          occupancy_grid.info.width, occupancy_grid.info.height);
  for (size_t i = 0; i < occupancy_grid.info.height; ++i) {
    for (size_t j = 0; j < occupancy_grid.info.width; ++j) {
      // Need to invert the ordering of the cells to
      // produce an image with pixel (0,0) in the top-left corner.
      int value = occupancy_grid.data[j + (occupancy_grid.info.height - i - 1) * occupancy_grid.info.width];
      if (value == 0) {
        fputc(254, pgm_file);
      } else if (value == +100) {
        fputc(000, pgm_file);
      } else {
        fputc(205, pgm_file);
      }
    }
  }
  fclose(pgm_file);
  return true;
}

bool SaveOccupancyGridMetadataToYamlFile(
    const std::string& map_name, const std::string& map_uuid,
    const std::string& map_directory, const nav_msgs::MapMetaData& map_metadata) {
  std::string map_directory_with_trailing_slash = map_directory;
  AddTrailingSlashToDirectoryPathIfNeeded(map_directory_with_trailing_slash);

  std::string image_name = map_name;
  if (image_name.empty())
    image_name = "\"\"";
  std::string uuid = map_uuid;
  if (uuid.empty())
    uuid = "\"\"";

  std::string map_yaml_file_path = map_directory_with_trailing_slash + map_name + ".yaml";
  FILE* yaml_file = fopen(map_yaml_file_path.c_str(), "w");
  if (!yaml_file) {
    LOG(ERROR) << "Could no open file " << map_yaml_file_path;
    return false;
  }

  tf::Matrix3x3 mat(tf::Quaternion(map_metadata.origin.orientation.x,
                                   map_metadata.origin.orientation.y,
                                   map_metadata.origin.orientation.z,
                                   map_metadata.origin.orientation.w));
  double yaw, pitch, roll;
  mat.getEulerYPR(yaw, pitch, roll);
  if (std::isnan(yaw))
    yaw = 0.0;

  fprintf(yaml_file, "image: %s.pgm\nresolution: %f\norigin: [%f, %f, %f]\nnegate: 0\n"
      "occupied_thresh: 0.65\nfree_thresh: 0.196\nuuid: %s\n\n",
      map_name.c_str(), map_metadata.resolution, map_metadata.origin.position.x,
      map_metadata.origin.position.y, yaw, uuid.c_str());

  fclose(yaml_file);
  return true;
}

bool LoadOccupancyGridFromFiles(
    const std::string&  map_name, const std::string& map_directory,
    nav_msgs::OccupancyGrid* occupancy_grid, std::string* map_uuid) {
  int negate;
  double occupied_threshold;
  double free_threshold;
  bool result = LoadOccupancyGridMetadataFromYamlFile(
      map_name, map_directory, &(occupancy_grid->info), &negate,
      &occupied_threshold, &free_threshold, map_uuid);
  return result && LoadOccupancyGridDataFromPgmFile(
      map_name, map_directory, negate, occupied_threshold, free_threshold,
      occupancy_grid);
}

bool LoadOccupancyGridDataFromPgmFile(
    const std::string&  map_name, const std::string& map_directory,
    bool negate, double occupied_threshold, double free_threshold,
    nav_msgs::OccupancyGrid* occupancy_grid) {
  std::string map_directory_with_trailing_slash = map_directory;
  AddTrailingSlashToDirectoryPathIfNeeded(map_directory_with_trailing_slash);

  std::string map_pgm_file_path = map_directory_with_trailing_slash + map_name + ".pgm";
  std::ifstream pgm_file(map_pgm_file_path, std::ios::binary);
  if (pgm_file.fail()) {
    LOG(ERROR) << "Could no open file " << map_pgm_file_path;
    return false;
  }

  // First line contains file type.
  std::string file_type;
  getline(pgm_file, file_type);
  LOG(INFO) << file_type;
  if (file_type.compare("P5") != 0) {
    LOG(ERROR) << "Pgm file type error. Type is " << file_type
        << " while supported type is P5.";
    return false;
  }
  // Second line contains comment.
  std::string comment;
  getline(pgm_file, comment);
  LOG(INFO) << comment;
  // Third line contains size.
  std::string image_size;
  getline(pgm_file, image_size);
  std::stringstream size_stream(image_size);
  size_stream >> occupancy_grid->info.width >> occupancy_grid->info.height;
  LOG(INFO) << "Image size is " << occupancy_grid->info.width << "x" << occupancy_grid->info.height;
  // Fourth line contains max value.
  std::string max_value_string;
  getline(pgm_file, max_value_string);
  std::stringstream max_val_stream(max_value_string);
  int max_value = 0;
  max_val_stream >> max_value;
  // Following lines contain binary data.
  int pixel_array[occupancy_grid->info.height * occupancy_grid->info.width];
  for (size_t i = 0; i < occupancy_grid->info.height * occupancy_grid->info.width; ++i) {
    char pixel = pgm_file.get();
    pixel_array[i] = static_cast<int>(pixel);
  }
  // Need to invert the ordering of the pixels to
  // produce a map with cell (0,0) in the lower-left corner.
  occupancy_grid->data.reserve(occupancy_grid->info.height * occupancy_grid->info.width);
  for (size_t i = 0; i < occupancy_grid->info.height; ++i) {
    for (size_t j = 0; j < occupancy_grid->info.width; ++j) {
      int value = pixel_array[j + (occupancy_grid->info.height - i - 1) * occupancy_grid->info.width];
      if (negate)
        value = max_value - value;
      double occupancy = (max_value - value) / static_cast<double>(max_value);
      if (occupancy < free_threshold) {
        occupancy_grid->data.push_back(0);
      } else if (occupancy > occupied_threshold) {
        occupancy_grid->data.push_back(100);
      } else {
        occupancy_grid->data.push_back(-1);
      }
    }
  }
  pgm_file.close();
  return true;
}

bool LoadOccupancyGridMetadataFromYamlFile(
    const std::string&  map_name, const std::string& map_directory,
    nav_msgs::MapMetaData* map_metadata, int* negate,
    double* occupied_threshold, double* free_threshold, std::string* map_uuid) {
  std::string map_directory_with_trailing_slash = map_directory;
  AddTrailingSlashToDirectoryPathIfNeeded(map_directory_with_trailing_slash);

  std::string map_yam_file_path = map_directory_with_trailing_slash + map_name + ".yaml";
  std::ifstream yaml_file(map_yam_file_path.c_str());
  if (yaml_file.fail()) {
    LOG(ERROR) << "Could no open file " << map_yam_file_path;
    return false;
  }

  YAML::Node node = YAML::Load(yaml_file);
  try {
    map_metadata->resolution = node["resolution"].as<double>();
  } catch (YAML::RepresentationException& e) {
    LOG(ERROR) << "The map does not contain a resolution tag or it is invalid. " << e.msg;
    return false;
  }
  try {
    *negate = node["negate"].as<int>();
  } catch (YAML::RepresentationException& e) {
    LOG(ERROR) << "The map does not contain a negate tag or it is invalid. " << e.msg;
    return false;
  }
  try {
    *occupied_threshold = node["occupied_thresh"].as<double>();
  } catch (YAML::RepresentationException& e) {
    LOG(ERROR) << "The map does not contain an occupied_thresh tag or it is invalid. " << e.msg;
    return false;
  }
  try {
    *free_threshold = node["free_thresh"].as<double>();
  } catch (YAML::RepresentationException& e) {
    LOG(ERROR) << "The map does not contain a free_thresh tag or it is invalid. " << e.msg;
    return false;
  }
  try {
    map_metadata->origin.position.x = node["origin"][0].as<double>();
    map_metadata->origin.position.y = node["origin"][1].as<double>();
    map_metadata->origin.position.z = 0.0;
    tf::Quaternion quaternion;
    double yaw = node["origin"][2].as<double>();
    quaternion.setRPY(0., 0., yaw);
    map_metadata->origin.orientation.x = quaternion.x();
    map_metadata->origin.orientation.y = quaternion.y();
    map_metadata->origin.orientation.z = quaternion.z();
    map_metadata->origin.orientation.w = quaternion.w();
  } catch (YAML::RepresentationException& e) {
    LOG(ERROR) << "The map does not contain an origin tag or it is invalid. " << e.msg;
    return false;
  }
  try {
    *map_uuid = node["uuid"].as<std::string>();
  } catch (YAML::RepresentationException& e) {
    LOG(WARNING) << "The map does not contain a uuid tag or it is invalid. " << e.msg;
  }
  yaml_file.close();
  return true;
}
} // namespace occupancy_grid_file_io
<commit_msg>Create occupancy grid directory if it does not exist<commit_after>// Copyright 2017 Intermodalics All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "tango_ros_native/occupancy_grid_file_io.h"

#include <cmath>
#include <fstream>
#include <iostream>
#include <sys/stat.h>
#include <unistd.h>

#include <geometry_msgs/Quaternion.h>
#include <tf/LinearMath/Matrix3x3.h>

#include <glog/logging.h>

#include <yaml-cpp/yaml.h>

namespace {
void AddTrailingSlashToDirectoryPathIfNeeded(std::string& directory_path) {
  if (!directory_path.empty() && directory_path.back() != '/') {
    directory_path += '/';
  }
}
bool DirectoryPathExists(const std::string& path) {
  struct stat st;
  if (stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFDIR)) {
    return true;
  } else {
    return false;
  }
}
int MakeDirectoryPath(const std::string& input_path, mode_t mode) {
  if (input_path.empty()) {
    // Path is empty, there is nothing to create so we return success.
    return 0;
  }

  size_t previous_slash_pos = 0;
  size_t current_slash_pos;
  std::string dir;
  int make_dir_status = 0;

  std::string path = input_path;
  CHECK(!path.empty());
  AddTrailingSlashToDirectoryPathIfNeeded(path);

  while ((current_slash_pos = path.find_first_of('/', previous_slash_pos)) !=
         std::string::npos) {
    dir = path.substr(0, current_slash_pos++);
    previous_slash_pos = current_slash_pos;
    if (dir.empty())
      continue;  // If leading / first time is 0 length.
    if (dir == ".")
      continue;

    if ((make_dir_status = mkdir(dir.c_str(), mode)) && errno != EEXIST) {
      return make_dir_status;
    }
  }
  return 0;
}
} // namespace

namespace occupancy_grid_file_io {
bool SaveOccupancyGridToFiles(
    const std::string& map_name, const std::string& map_uuid,
    const std::string& map_directory, const nav_msgs::OccupancyGrid& occupancy_grid) {
  return SaveOccupancyGridDataToPgmFile(map_name, map_directory, occupancy_grid)
      && SaveOccupancyGridMetadataToYamlFile(map_name, map_uuid, map_directory, occupancy_grid.info);
}

bool SaveOccupancyGridDataToPgmFile(
    const std::string& map_name, const std::string& map_directory,
    const nav_msgs::OccupancyGrid& occupancy_grid) {
  std::string map_directory_with_trailing_slash = map_directory;
  AddTrailingSlashToDirectoryPathIfNeeded(map_directory_with_trailing_slash);
  if (!DirectoryPathExists(map_directory_with_trailing_slash)) {
    LOG(INFO) << "Directory " << map_directory_with_trailing_slash << " does not exist, creating it now.";
    if (MakeDirectoryPath(map_directory_with_trailing_slash, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
      LOG(ERROR) << "Could not create directory: " << map_directory_with_trailing_slash;
      return false;
    }
  }

  std::string map_pgm_file_path = map_directory_with_trailing_slash + map_name + ".pgm";
  FILE* pgm_file = fopen(map_pgm_file_path.c_str(), "w");
  if (!pgm_file) {
    LOG(ERROR) << "Could no open file " << map_pgm_file_path;
    return false;
  }

  fprintf(pgm_file, "P5\n# CREATOR: TangoRosStreamer %.3f m/pix\n%d %d\n255\n",
          occupancy_grid.info.resolution,
          occupancy_grid.info.width, occupancy_grid.info.height);
  for (size_t i = 0; i < occupancy_grid.info.height; ++i) {
    for (size_t j = 0; j < occupancy_grid.info.width; ++j) {
      // Need to invert the ordering of the cells to
      // produce an image with pixel (0,0) in the top-left corner.
      int value = occupancy_grid.data[j + (occupancy_grid.info.height - i - 1) * occupancy_grid.info.width];
      if (value == 0) {
        fputc(254, pgm_file);
      } else if (value == +100) {
        fputc(000, pgm_file);
      } else {
        fputc(205, pgm_file);
      }
    }
  }
  fclose(pgm_file);
  LOG(INFO) << "Map image successfully saved to " << map_pgm_file_path;
  return true;
}

bool SaveOccupancyGridMetadataToYamlFile(
    const std::string& map_name, const std::string& map_uuid,
    const std::string& map_directory, const nav_msgs::MapMetaData& map_metadata) {
  std::string map_directory_with_trailing_slash = map_directory;
  AddTrailingSlashToDirectoryPathIfNeeded(map_directory_with_trailing_slash);
  if (!DirectoryPathExists(map_directory_with_trailing_slash)) {
    LOG(INFO) << "Directory " << map_directory_with_trailing_slash << " does not exist, creating it now.";
    if (MakeDirectoryPath(map_directory_with_trailing_slash, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
      LOG(ERROR) << "Could not create directory: " << map_directory_with_trailing_slash;
      return false;
    }
  }

  std::string image_name = map_name;
  if (image_name.empty())
    image_name = "\"\"";
  std::string uuid = map_uuid;
  if (uuid.empty())
    uuid = "\"\"";

  std::string map_yaml_file_path = map_directory_with_trailing_slash + map_name + ".yaml";
  FILE* yaml_file = fopen(map_yaml_file_path.c_str(), "w");
  if (!yaml_file) {
    LOG(ERROR) << "Could no open file " << map_yaml_file_path;
    return false;
  }

  tf::Matrix3x3 mat(tf::Quaternion(map_metadata.origin.orientation.x,
                                   map_metadata.origin.orientation.y,
                                   map_metadata.origin.orientation.z,
                                   map_metadata.origin.orientation.w));
  double yaw, pitch, roll;
  mat.getEulerYPR(yaw, pitch, roll);
  if (std::isnan(yaw))
    yaw = 0.0;

  fprintf(yaml_file, "image: %s.pgm\nresolution: %f\norigin: [%f, %f, %f]\nnegate: 0\n"
      "occupied_thresh: 0.65\nfree_thresh: 0.196\nuuid: %s\n\n",
      map_name.c_str(), map_metadata.resolution, map_metadata.origin.position.x,
      map_metadata.origin.position.y, yaw, uuid.c_str());

  fclose(yaml_file);
  LOG(INFO) << "Map yaml file successfully saved to " << map_yaml_file_path;
  return true;
}

bool LoadOccupancyGridFromFiles(
    const std::string&  map_name, const std::string& map_directory,
    nav_msgs::OccupancyGrid* occupancy_grid, std::string* map_uuid) {
  int negate;
  double occupied_threshold;
  double free_threshold;
  bool result = LoadOccupancyGridMetadataFromYamlFile(
      map_name, map_directory, &(occupancy_grid->info), &negate,
      &occupied_threshold, &free_threshold, map_uuid);
  return result && LoadOccupancyGridDataFromPgmFile(
      map_name, map_directory, negate, occupied_threshold, free_threshold,
      occupancy_grid);
}

bool LoadOccupancyGridDataFromPgmFile(
    const std::string&  map_name, const std::string& map_directory,
    bool negate, double occupied_threshold, double free_threshold,
    nav_msgs::OccupancyGrid* occupancy_grid) {
  std::string map_directory_with_trailing_slash = map_directory;
  AddTrailingSlashToDirectoryPathIfNeeded(map_directory_with_trailing_slash);

  std::string map_pgm_file_path = map_directory_with_trailing_slash + map_name + ".pgm";
  std::ifstream pgm_file(map_pgm_file_path, std::ios::binary);
  if (pgm_file.fail()) {
    LOG(ERROR) << "Could no open file " << map_pgm_file_path;
    return false;
  }

  // First line contains file type.
  std::string file_type;
  getline(pgm_file, file_type);
  LOG(INFO) << file_type;
  if (file_type.compare("P5") != 0) {
    LOG(ERROR) << "Pgm file type error. Type is " << file_type
        << " while supported type is P5.";
    return false;
  }
  // Second line contains comment.
  std::string comment;
  getline(pgm_file, comment);
  LOG(INFO) << comment;
  // Third line contains size.
  std::string image_size;
  getline(pgm_file, image_size);
  std::stringstream size_stream(image_size);
  size_stream >> occupancy_grid->info.width >> occupancy_grid->info.height;
  LOG(INFO) << "Image size is " << occupancy_grid->info.width << "x" << occupancy_grid->info.height;
  // Fourth line contains max value.
  std::string max_value_string;
  getline(pgm_file, max_value_string);
  std::stringstream max_val_stream(max_value_string);
  int max_value = 0;
  max_val_stream >> max_value;
  // Following lines contain binary data.
  int pixel_array[occupancy_grid->info.height * occupancy_grid->info.width];
  for (size_t i = 0; i < occupancy_grid->info.height * occupancy_grid->info.width; ++i) {
    char pixel = pgm_file.get();
    pixel_array[i] = static_cast<int>(pixel);
  }
  // Need to invert the ordering of the pixels to
  // produce a map with cell (0,0) in the lower-left corner.
  occupancy_grid->data.reserve(occupancy_grid->info.height * occupancy_grid->info.width);
  for (size_t i = 0; i < occupancy_grid->info.height; ++i) {
    for (size_t j = 0; j < occupancy_grid->info.width; ++j) {
      int value = pixel_array[j + (occupancy_grid->info.height - i - 1) * occupancy_grid->info.width];
      if (negate)
        value = max_value - value;
      double occupancy = (max_value - value) / static_cast<double>(max_value);
      if (occupancy < free_threshold) {
        occupancy_grid->data.push_back(0);
      } else if (occupancy > occupied_threshold) {
        occupancy_grid->data.push_back(100);
      } else {
        occupancy_grid->data.push_back(-1);
      }
    }
  }
  pgm_file.close();
  LOG(INFO) << "Map image successfully loaded from " << map_pgm_file_path;
  return true;
}

bool LoadOccupancyGridMetadataFromYamlFile(
    const std::string&  map_name, const std::string& map_directory,
    nav_msgs::MapMetaData* map_metadata, int* negate,
    double* occupied_threshold, double* free_threshold, std::string* map_uuid) {
  std::string map_directory_with_trailing_slash = map_directory;
  AddTrailingSlashToDirectoryPathIfNeeded(map_directory_with_trailing_slash);

  std::string map_yam_file_path = map_directory_with_trailing_slash + map_name + ".yaml";
  std::ifstream yaml_file(map_yam_file_path.c_str());
  if (yaml_file.fail()) {
    LOG(ERROR) << "Could no open file " << map_yam_file_path;
    return false;
  }

  YAML::Node node = YAML::Load(yaml_file);
  try {
    map_metadata->resolution = node["resolution"].as<double>();
  } catch (YAML::RepresentationException& e) {
    LOG(ERROR) << "The map does not contain a resolution tag or it is invalid. " << e.msg;
    return false;
  }
  try {
    *negate = node["negate"].as<int>();
  } catch (YAML::RepresentationException& e) {
    LOG(ERROR) << "The map does not contain a negate tag or it is invalid. " << e.msg;
    return false;
  }
  try {
    *occupied_threshold = node["occupied_thresh"].as<double>();
  } catch (YAML::RepresentationException& e) {
    LOG(ERROR) << "The map does not contain an occupied_thresh tag or it is invalid. " << e.msg;
    return false;
  }
  try {
    *free_threshold = node["free_thresh"].as<double>();
  } catch (YAML::RepresentationException& e) {
    LOG(ERROR) << "The map does not contain a free_thresh tag or it is invalid. " << e.msg;
    return false;
  }
  try {
    map_metadata->origin.position.x = node["origin"][0].as<double>();
    map_metadata->origin.position.y = node["origin"][1].as<double>();
    map_metadata->origin.position.z = 0.0;
    tf::Quaternion quaternion;
    double yaw = node["origin"][2].as<double>();
    quaternion.setRPY(0., 0., yaw);
    map_metadata->origin.orientation.x = quaternion.x();
    map_metadata->origin.orientation.y = quaternion.y();
    map_metadata->origin.orientation.z = quaternion.z();
    map_metadata->origin.orientation.w = quaternion.w();
  } catch (YAML::RepresentationException& e) {
    LOG(ERROR) << "The map does not contain an origin tag or it is invalid. " << e.msg;
    return false;
  }
  try {
    *map_uuid = node["uuid"].as<std::string>();
  } catch (YAML::RepresentationException& e) {
    LOG(WARNING) << "The map does not contain a uuid tag or it is invalid. " << e.msg;
  }
  yaml_file.close();
  LOG(INFO) << "Map yaml file successfully loaded from " << map_yam_file_path;
  return true;
}
} // namespace occupancy_grid_file_io
<|endoftext|>


<commit_before>#include <cctype>
#include <cstdlib>
#include <cstring>
#include <stdio.h>

#include <v8.h>

#include <node.h>
#include <node_buffer.h>

#include <openssl/bn.h>
#include <openssl/buffer.h>
#include <openssl/ecdsa.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/ripemd.h>

using namespace std;
using namespace v8;
using namespace node;

static Handle<Value> VException(const char *msg) {
    HandleScope scope;
    return ThrowException(Exception::Error(String::New(msg)));
}

static Handle<Value>
new_keypair (const Arguments& args)
{
  HandleScope scope;
  EC_KEY* pkey;

  // Generate
  pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
  if (pkey == NULL) {
    return VException("Error from EC_KEY_new_by_curve_name");
  }
  if (!EC_KEY_generate_key(pkey)) {
    return VException("Error from EC_KEY_generate_key");
  }

  // Export private
  unsigned int priv_size = i2d_ECPrivateKey(pkey, NULL);
  if (!priv_size) {
    return VException("Error from i2d_ECPrivateKey(pkey, NULL)");
  }
  unsigned char *priv = (unsigned char *)malloc(priv_size);
  if (i2d_ECPrivateKey(pkey, &priv) != priv_size) {
   return VException("Error from i2d_ECPrivateKey(pkey, &priv)");
  }

  // Export public
  unsigned int pub_size = i2o_ECPublicKey(pkey, NULL);
  if (!pub_size) {
    return VException("Error from i2o_ECPublicKey(pkey, NULL)");
  }
  unsigned char *pub = (unsigned char *)malloc(pub_size);
  if (i2o_ECPublicKey(pkey, &pub) != pub_size) {
    return VException("Error from i2o_ECPublicKey(pkey, &pub)");
  }

  // Return [priv_buf, pub_buf]

  Local<Array> a = Array::New(2);

  Buffer *priv_buf = Buffer::New(priv_size);
  memcpy(Buffer::Data(priv_buf), priv, priv_size);
  a->Set(Integer::New(0), priv_buf->handle_);

  Buffer *pub_buf = Buffer::New(pub_size);
  memcpy(Buffer::Data(pub_buf), pub, pub_size);
  a->Set(Integer::New(1), pub_buf->handle_);

  EC_KEY_free(pkey);
  free(priv);
  free(pub);

  return scope.Close(a);
}

static Handle<Value>
pubkey_to_address256 (const Arguments& args)
{
  HandleScope scope;

  if (args.Length() != 1) {
    return VException("One argument expected: pubkey Buffer");
  }
  if (!Buffer::HasInstance(args[0])) {
    return VException("One argument expected: pubkey Buffer");
  }
  v8::Handle<v8::Object> pub_buf = args[0]->ToObject();

  unsigned char *pub_data = (unsigned char *) Buffer::Data(pub_buf);

  // sha256(pubkey)
  unsigned char hash1[SHA256_DIGEST_LENGTH];
  SHA256_CTX c;
  SHA256_Init(&c);
  SHA256_Update(&c, pub_data, Buffer::Length(pub_buf));
  SHA256_Final(hash1, &c);

  // ripemd160(sha256(pubkey))
  unsigned char hash2[RIPEMD160_DIGEST_LENGTH];
  RIPEMD160_CTX c2;
  RIPEMD160_Init(&c2);
  RIPEMD160_Update(&c2, hash1, SHA256_DIGEST_LENGTH);
  RIPEMD160_Final(hash2, &c2);

  // x = '\x00' + ripemd160(sha256(pubkey))
  // LATER: make the version an optional argument
  unsigned char address256[1 + RIPEMD160_DIGEST_LENGTH + 4];
  address256[0] = 0;
  memcpy(address256 + 1, hash2, RIPEMD160_DIGEST_LENGTH);

  // sha256(x)
  unsigned char hash3[SHA256_DIGEST_LENGTH];
  SHA256_CTX c3;
  SHA256_Init(&c3);
  SHA256_Update(&c3, address256, 1 + RIPEMD160_DIGEST_LENGTH);
  SHA256_Final(hash3, &c3);

  // address256 = (x + sha256(x)[:4])
  memcpy(
    address256 + (1 + RIPEMD160_DIGEST_LENGTH),
    hash3,
    4);

  Buffer *address256_buf = Buffer::New(1 + RIPEMD160_DIGEST_LENGTH + 4);
  memcpy(Buffer::Data(address256_buf), address256, 1 + RIPEMD160_DIGEST_LENGTH + 4);
  return scope.Close(address256_buf->handle_);
}

static const char* BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

static Handle<Value>
base58_encode (const Arguments& args)
{
  HandleScope scope;

  if (args.Length() != 1) {
    return VException("One argument expected: a Buffer");
  }
  if (!Buffer::HasInstance(args[0])) {
    return VException("One argument expected: a Buffer");
  }
  v8::Handle<v8::Object> buf = args[0]->ToObject();

  unsigned char *buf_data = (unsigned char *) Buffer::Data(buf);
  int buf_length = Buffer::Length(buf);

  BN_CTX *ctx = BN_CTX_new();

  BIGNUM *bn = BN_bin2bn(buf_data, buf_length, NULL);

  BIGNUM *bn58 = BN_new();
  BN_set_word(bn58, 58);

  BIGNUM *bn0 = BN_new();
  BN_set_word(bn0, 0);

  BIGNUM *dv = BN_new();
  BIGNUM *rem = BN_new();

  // TODO: compute safe length
  char *str = new char[100];
  unsigned int c;
  int i, j, j2;

  i = 0;
  while (BN_cmp(bn, bn0) > 0) {
    if (!BN_div(dv, rem, bn, bn58, ctx)) {
      return VException("BN_div failed");
    }
    bn = dv;
    c = BN_get_word(rem);
    str[i] = BASE58_ALPHABET[c];
    i++;
  }

  // Leading zeros
  for (j = 0; j < buf_length; j++) {
    if (buf_data[j] != 0) {
      break;
    }
    str[i] = BASE58_ALPHABET[0];
    i++;
  }

  // Terminator
  str[i] = 0;

  // Reverse string
  int numSwaps = (i / 2);
  char tmp;
  for (j = 0; j < numSwaps; j++) {
    j2 = i - 1 - j;
    tmp = str[j];
    str[j] = str[j2];
    str[j2] = tmp;
  }

  BN_free(bn);
  BN_free(bn58);
  BN_free(bn0);
  BN_free(dv);
  BN_free(rem);
  BN_CTX_free(ctx);

  Local<String> ret = String::New(str);
  delete [] str;
  return scope.Close(ret);
}

static Handle<Value>
base58_decode (const Arguments& args)
{
  HandleScope scope;

  if (args.Length() != 1) {
    return VException("One argument expected: a String");
  }
  if (!args[0]->IsString()) {
    return VException("One argument expected: a String");
  }

  BN_CTX *ctx = BN_CTX_new();

  BIGNUM *bn58 = BN_new();
  BN_set_word(bn58, 58);

  BIGNUM *bn = BN_new();
  BN_set_word(bn, 0);

  BIGNUM *bnChar = BN_new();

  String::Utf8Value str(args[0]->ToString());
  char *psz = *str;

  while (isspace(*psz))
    psz++;

  // Convert big endian string to bignum
  for (const char* p = psz; *p; p++) {
    const char* p1 = strchr(BASE58_ALPHABET, *p);
    if (p1 == NULL) {
      while (isspace(*p))
        p++;
      if (*p != '\0')
        return VException("Error");
      break;
    }
    BN_set_word(bnChar, p1 - BASE58_ALPHABET);
    if (!BN_mul(bn, bn, bn58, ctx))
      return VException("BN_mul failed");
    if (!BN_add(bn, bn, bnChar))
      return VException("BN_add failed");
  }

  // Get bignum as little endian data
  unsigned int tmpLen = BN_num_bytes(bn);
  unsigned char *tmp = (unsigned char *)malloc(tmpLen);
  BN_bn2bin(bn, tmp);

  // Trim off sign byte if present
  if (tmpLen >= 2 && tmp[tmpLen-1] == 0 && tmp[tmpLen-2] >= 0x80)
    tmpLen--;

  // Restore leading zeros
  int nLeadingZeros = 0;
  for (const char* p = psz; *p == BASE58_ALPHABET[0]; p++)
    nLeadingZeros++;

  // Allocate buffer and zero it
  Buffer *buf = Buffer::New(nLeadingZeros + tmpLen);
  char* data = Buffer::Data(buf);
  memset(data, 0, nLeadingZeros + tmpLen);
  memcpy(data+nLeadingZeros, tmp, tmpLen);

  BN_free(bn58);
  BN_free(bn);
  BN_free(bnChar);
  BN_CTX_free(ctx);
  free(tmp);

  return scope.Close(buf->handle_);
}

extern "C" void
init (Handle<Object> target)
{
  HandleScope scope;
  target->Set(String::New("new_keypair"), FunctionTemplate::New(new_keypair)->GetFunction());
  target->Set(String::New("pubkey_to_address256"), FunctionTemplate::New(pubkey_to_address256)->GetFunction());
  target->Set(String::New("base58_encode"), FunctionTemplate::New(base58_encode)->GetFunction());
  target->Set(String::New("base58_decode"), FunctionTemplate::New(base58_decode)->GetFunction());
}
<commit_msg>Fixed double free in base58_encode().<commit_after>#include <cctype>
#include <cstdlib>
#include <cstring>
#include <stdio.h>

#include <v8.h>

#include <node.h>
#include <node_buffer.h>

#include <openssl/bn.h>
#include <openssl/buffer.h>
#include <openssl/ecdsa.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/ripemd.h>

using namespace std;
using namespace v8;
using namespace node;

static Handle<Value> VException(const char *msg) {
    HandleScope scope;
    return ThrowException(Exception::Error(String::New(msg)));
}

static Handle<Value>
new_keypair (const Arguments& args)
{
  HandleScope scope;
  EC_KEY* pkey;

  // Generate
  pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
  if (pkey == NULL) {
    return VException("Error from EC_KEY_new_by_curve_name");
  }
  if (!EC_KEY_generate_key(pkey)) {
    return VException("Error from EC_KEY_generate_key");
  }

  // Export private
  unsigned int priv_size = i2d_ECPrivateKey(pkey, NULL);
  if (!priv_size) {
    return VException("Error from i2d_ECPrivateKey(pkey, NULL)");
  }
  unsigned char *priv = (unsigned char *)malloc(priv_size);
  if (i2d_ECPrivateKey(pkey, &priv) != priv_size) {
   return VException("Error from i2d_ECPrivateKey(pkey, &priv)");
  }

  // Export public
  unsigned int pub_size = i2o_ECPublicKey(pkey, NULL);
  if (!pub_size) {
    return VException("Error from i2o_ECPublicKey(pkey, NULL)");
  }
  unsigned char *pub = (unsigned char *)malloc(pub_size);
  if (i2o_ECPublicKey(pkey, &pub) != pub_size) {
    return VException("Error from i2o_ECPublicKey(pkey, &pub)");
  }

  // Return [priv_buf, pub_buf]

  Local<Array> a = Array::New(2);

  Buffer *priv_buf = Buffer::New(priv_size);
  memcpy(Buffer::Data(priv_buf), priv, priv_size);
  a->Set(Integer::New(0), priv_buf->handle_);

  Buffer *pub_buf = Buffer::New(pub_size);
  memcpy(Buffer::Data(pub_buf), pub, pub_size);
  a->Set(Integer::New(1), pub_buf->handle_);

  EC_KEY_free(pkey);
  free(priv);
  free(pub);

  return scope.Close(a);
}

static Handle<Value>
pubkey_to_address256 (const Arguments& args)
{
  HandleScope scope;

  if (args.Length() != 1) {
    return VException("One argument expected: pubkey Buffer");
  }
  if (!Buffer::HasInstance(args[0])) {
    return VException("One argument expected: pubkey Buffer");
  }
  v8::Handle<v8::Object> pub_buf = args[0]->ToObject();

  unsigned char *pub_data = (unsigned char *) Buffer::Data(pub_buf);

  // sha256(pubkey)
  unsigned char hash1[SHA256_DIGEST_LENGTH];
  SHA256_CTX c;
  SHA256_Init(&c);
  SHA256_Update(&c, pub_data, Buffer::Length(pub_buf));
  SHA256_Final(hash1, &c);

  // ripemd160(sha256(pubkey))
  unsigned char hash2[RIPEMD160_DIGEST_LENGTH];
  RIPEMD160_CTX c2;
  RIPEMD160_Init(&c2);
  RIPEMD160_Update(&c2, hash1, SHA256_DIGEST_LENGTH);
  RIPEMD160_Final(hash2, &c2);

  // x = '\x00' + ripemd160(sha256(pubkey))
  // LATER: make the version an optional argument
  unsigned char address256[1 + RIPEMD160_DIGEST_LENGTH + 4];
  address256[0] = 0;
  memcpy(address256 + 1, hash2, RIPEMD160_DIGEST_LENGTH);

  // sha256(x)
  unsigned char hash3[SHA256_DIGEST_LENGTH];
  SHA256_CTX c3;
  SHA256_Init(&c3);
  SHA256_Update(&c3, address256, 1 + RIPEMD160_DIGEST_LENGTH);
  SHA256_Final(hash3, &c3);

  // address256 = (x + sha256(x)[:4])
  memcpy(
    address256 + (1 + RIPEMD160_DIGEST_LENGTH),
    hash3,
    4);

  Buffer *address256_buf = Buffer::New(1 + RIPEMD160_DIGEST_LENGTH + 4);
  memcpy(Buffer::Data(address256_buf), address256, 1 + RIPEMD160_DIGEST_LENGTH + 4);
  return scope.Close(address256_buf->handle_);
}

static const char* BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

static Handle<Value>
base58_encode (const Arguments& args)
{
  HandleScope scope;

  if (args.Length() != 1) {
    return VException("One argument expected: a Buffer");
  }
  if (!Buffer::HasInstance(args[0])) {
    return VException("One argument expected: a Buffer");
  }
  v8::Handle<v8::Object> buf = args[0]->ToObject();

  unsigned char *buf_data = (unsigned char *) Buffer::Data(buf);
  int buf_length = Buffer::Length(buf);

  BN_CTX *ctx = BN_CTX_new();

  BIGNUM *bn = BN_bin2bn(buf_data, buf_length, NULL);

  BIGNUM *bn58 = BN_new();
  BN_set_word(bn58, 58);

  BIGNUM *bn0 = BN_new();
  BN_set_word(bn0, 0);

  BIGNUM *dv = BN_new();
  BIGNUM *rem = BN_new();

  // TODO: compute safe length
  char *str = new char[100];
  unsigned int c;
  int i, j, j2;

  i = 0;
  while (BN_cmp(bn, bn0) > 0) {
    if (!BN_div(dv, rem, bn, bn58, ctx)) {
      return VException("BN_div failed");
    }
    if (bn != dv) {
      BN_free(bn);
      bn = dv;
    }
    c = BN_get_word(rem);
    str[i] = BASE58_ALPHABET[c];
    i++;
  }

  // Leading zeros
  for (j = 0; j < buf_length; j++) {
    if (buf_data[j] != 0) {
      break;
    }
    str[i] = BASE58_ALPHABET[0];
    i++;
  }

  // Terminator
  str[i] = 0;

  // Reverse string
  int numSwaps = (i / 2);
  char tmp;
  for (j = 0; j < numSwaps; j++) {
    j2 = i - 1 - j;
    tmp = str[j];
    str[j] = str[j2];
    str[j2] = tmp;
  }

  BN_free(bn);
  BN_free(bn58);
  BN_free(bn0);
  BN_free(rem);
  BN_CTX_free(ctx);

  Local<String> ret = String::New(str);
  delete [] str;
  return scope.Close(ret);
}

static Handle<Value>
base58_decode (const Arguments& args)
{
  HandleScope scope;

  if (args.Length() != 1) {
    return VException("One argument expected: a String");
  }
  if (!args[0]->IsString()) {
    return VException("One argument expected: a String");
  }

  BN_CTX *ctx = BN_CTX_new();

  BIGNUM *bn58 = BN_new();
  BN_set_word(bn58, 58);

  BIGNUM *bn = BN_new();
  BN_set_word(bn, 0);

  BIGNUM *bnChar = BN_new();

  String::Utf8Value str(args[0]->ToString());
  char *psz = *str;

  while (isspace(*psz))
    psz++;

  // Convert big endian string to bignum
  for (const char* p = psz; *p; p++) {
    const char* p1 = strchr(BASE58_ALPHABET, *p);
    if (p1 == NULL) {
      while (isspace(*p))
        p++;
      if (*p != '\0')
        return VException("Error");
      break;
    }
    BN_set_word(bnChar, p1 - BASE58_ALPHABET);
    if (!BN_mul(bn, bn, bn58, ctx))
      return VException("BN_mul failed");
    if (!BN_add(bn, bn, bnChar))
      return VException("BN_add failed");
  }

  // Get bignum as little endian data
  unsigned int tmpLen = BN_num_bytes(bn);
  unsigned char *tmp = (unsigned char *)malloc(tmpLen);
  BN_bn2bin(bn, tmp);

  // Trim off sign byte if present
  if (tmpLen >= 2 && tmp[tmpLen-1] == 0 && tmp[tmpLen-2] >= 0x80)
    tmpLen--;

  // Restore leading zeros
  int nLeadingZeros = 0;
  for (const char* p = psz; *p == BASE58_ALPHABET[0]; p++)
    nLeadingZeros++;

  // Allocate buffer and zero it
  Buffer *buf = Buffer::New(nLeadingZeros + tmpLen);
  char* data = Buffer::Data(buf);
  memset(data, 0, nLeadingZeros + tmpLen);
  memcpy(data+nLeadingZeros, tmp, tmpLen);

  BN_free(bn58);
  BN_free(bn);
  BN_free(bnChar);
  BN_CTX_free(ctx);
  free(tmp);

  return scope.Close(buf->handle_);
}

extern "C" void
init (Handle<Object> target)
{
  HandleScope scope;
  target->Set(String::New("new_keypair"), FunctionTemplate::New(new_keypair)->GetFunction());
  target->Set(String::New("pubkey_to_address256"), FunctionTemplate::New(pubkey_to_address256)->GetFunction());
  target->Set(String::New("base58_encode"), FunctionTemplate::New(base58_encode)->GetFunction());
  target->Set(String::New("base58_decode"), FunctionTemplate::New(base58_decode)->GetFunction());
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtest.h>
#include <QtDeclarative/qdeclarativeengine.h>
#include <QtDeclarative/qdeclarativecomponent.h>
#include <QtDeclarative/qsgview.h>
#include <private/qsgrectangle_p.h>
#include <private/qsgimage_p.h>
#include <private/qsganimatedimage_p.h>
#include <QSignalSpy>
#include <QtDeclarative/qdeclarativecontext.h>

#include "../shared/testhttpserver.h"
#include "../../../shared/util.h"

#ifdef Q_OS_SYMBIAN
// In Symbian OS test data is located in applications private dir
#define SRCDIR "."
#endif

class tst_qsganimatedimage : public QObject
{
    Q_OBJECT
public:
    tst_qsganimatedimage() {}

private slots:
    void play();
    void pause();
    void stopped();
    void setFrame();
    void frameCount();
    void mirror_running();
    void mirror_notRunning();
    void mirror_notRunning_data();
    void remote();
    void remote_data();
    void sourceSize();
    void sourceSizeReadOnly();
    void invalidSource();
    void qtbug_16520();
    void progressAndStatusChanges();

};

void tst_qsganimatedimage::play()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickman.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QVERIFY(anim->isPlaying());

    delete anim;
}

void tst_qsganimatedimage::pause()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanpause.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QVERIFY(anim->isPlaying());
    QVERIFY(anim->isPaused());

    delete anim;
}

void tst_qsganimatedimage::stopped()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanstopped.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QVERIFY(!anim->isPlaying());
    QCOMPARE(anim->currentFrame(), 0);

    delete anim;
}

void tst_qsganimatedimage::setFrame()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanpause.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QVERIFY(anim->isPlaying());
    QCOMPARE(anim->currentFrame(), 2);

    delete anim;
}

void tst_qsganimatedimage::frameCount()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/colors.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QVERIFY(anim->isPlaying());
    QCOMPARE(anim->frameCount(), 3);

    delete anim;
}

void tst_qsganimatedimage::mirror_running()
{
    // test where mirror is set to true after animation has started

    QSGView *canvas = new QSGView;
    canvas->show();

    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/hearts.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(canvas->rootObject());
    QVERIFY(anim);

    int width = anim->property("width").toInt();

    QCOMPARE(anim->currentFrame(), 0);
    QPixmap frame0 = canvas->renderPixmap();
    anim->setCurrentFrame(1);
    QPixmap frame1 = canvas->renderPixmap();

    anim->setCurrentFrame(0);

    QSignalSpy spy(anim, SIGNAL(frameChanged()));
    anim->setPlaying(true);

    QTRY_VERIFY(spy.count() == 1); spy.clear();
    anim->setProperty("mirror", true);

    QCOMPARE(anim->currentFrame(), 1);
    QPixmap frame1_flipped = canvas->renderPixmap();

    QTRY_VERIFY(spy.count() == 1); spy.clear();
    QCOMPARE(anim->currentFrame(), 0);  // animation only has 2 frames, should cycle back to first
    QPixmap frame0_flipped = canvas->renderPixmap();

    QTransform transform;
    transform.translate(width, 0).scale(-1, 1.0);
    QPixmap frame0_expected = frame0.transformed(transform);
    QPixmap frame1_expected = frame1.transformed(transform);

    QCOMPARE(frame0_flipped, frame0_expected);
    QCOMPARE(frame1_flipped, frame1_expected);

    delete canvas;
}

void tst_qsganimatedimage::mirror_notRunning()
{
    QFETCH(QUrl, fileUrl);

    QSGView *canvas = new QSGView;
    canvas->show();

    canvas->setSource(fileUrl);
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(canvas->rootObject());
    QVERIFY(anim);

    int width = anim->property("width").toInt();
    QPixmap screenshot = canvas->renderPixmap();

    QTransform transform;
    transform.translate(width, 0).scale(-1, 1.0);
    QPixmap expected = screenshot.transformed(transform);

    int frame = anim->currentFrame();
    bool playing = anim->isPlaying();
    bool paused = anim->isPlaying();

    anim->setProperty("mirror", true);
    screenshot = canvas->renderPixmap();

    QSKIP("Skip while QTBUG-19351 and QTBUG-19252 are not resolved", SkipSingle);
    QCOMPARE(screenshot, expected);

    // mirroring should not change the current frame or playing status
    QCOMPARE(anim->currentFrame(), frame);
    QCOMPARE(anim->isPlaying(), playing);
    QCOMPARE(anim->isPaused(), paused);

    delete canvas;
}

void tst_qsganimatedimage::mirror_notRunning_data()
{
    QTest::addColumn<QUrl>("fileUrl");

    QTest::newRow("paused") << QUrl::fromLocalFile(SRCDIR "/data/stickmanpause.qml");
    QTest::newRow("stopped") << QUrl::fromLocalFile(SRCDIR "/data/stickmanstopped.qml");
}

void tst_qsganimatedimage::remote()
{
    QFETCH(QString, fileName);
    QFETCH(bool, paused);

    TestHTTPServer server(14449);
    QVERIFY(server.isValid());
    server.serveDirectory(SRCDIR "/data");

    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl("http://127.0.0.1:14449/" + fileName));
    QTRY_VERIFY(component.isReady());

    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);

    QTRY_VERIFY(anim->isPlaying());
    if (paused) {
        QTRY_VERIFY(anim->isPaused());
        QCOMPARE(anim->currentFrame(), 2);
    }
    QVERIFY(anim->status() != QSGAnimatedImage::Error);

    delete anim;
}

void tst_qsganimatedimage::sourceSize()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanscaled.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QCOMPARE(anim->width(),240.0);
    QCOMPARE(anim->height(),180.0);
    QCOMPARE(anim->sourceSize(),QSize(160,120));

    delete anim;
}

void tst_qsganimatedimage::sourceSizeReadOnly()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanerror1.qml"));
    QVERIFY(component.isError());
    QCOMPARE(component.errors().at(0).description(), QString("Invalid property assignment: \"sourceSize\" is a read-only property"));
}

void tst_qsganimatedimage::remote_data()
{
    QTest::addColumn<QString>("fileName");
    QTest::addColumn<bool>("paused");

    QTest::newRow("playing") << "stickman.qml" << false;
    QTest::newRow("paused") << "stickmanpause.qml" << true;
}

void tst_qsganimatedimage::invalidSource()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine);
    component.setData("import QtQuick 2.0\n AnimatedImage { source: \"no-such-file.gif\" }", QUrl::fromLocalFile(""));
    QVERIFY(component.isReady());

    QTest::ignoreMessage(QtWarningMsg, "file::2:2: QML AnimatedImage: Error Reading Animated Image File file:no-such-file.gif");

    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);

    QVERIFY(!anim->isPlaying());
    QVERIFY(!anim->isPaused());
    QCOMPARE(anim->currentFrame(), 0);
    QCOMPARE(anim->frameCount(), 0);
    QTRY_VERIFY(anim->status() == 3);
}

void tst_qsganimatedimage::qtbug_16520()
{
    TestHTTPServer server(14449);
    QVERIFY(server.isValid());
    server.serveDirectory(SRCDIR "/data");

    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/qtbug-16520.qml"));
    QTRY_VERIFY(component.isReady());

    QSGRectangle *root = qobject_cast<QSGRectangle *>(component.create());
    QVERIFY(root);
    QSGAnimatedImage *anim = root->findChild<QSGAnimatedImage*>("anim");

    anim->setProperty("source", "http://127.0.0.1:14449/stickman.gif");

    QTRY_VERIFY(anim->opacity() == 0);
    QTRY_VERIFY(anim->opacity() == 1);

    delete anim;
}

void tst_qsganimatedimage::progressAndStatusChanges()
{
    TestHTTPServer server(14449);
    QVERIFY(server.isValid());
    server.serveDirectory(SRCDIR "/data");

    QDeclarativeEngine engine;
    QString componentStr = "import QtQuick 2.0\nAnimatedImage { source: srcImage }";
    QDeclarativeContext *ctxt = engine.rootContext();
    ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/stickman.gif"));
    QDeclarativeComponent component(&engine);
    component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
    QSGImage *obj = qobject_cast<QSGImage*>(component.create());
    QVERIFY(obj != 0);
    QVERIFY(obj->status() == QSGImage::Ready);
    QTRY_VERIFY(obj->progress() == 1.0);

    QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(const QUrl &)));
    QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal)));
    QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QSGImageBase::Status)));

    // Loading local file
    ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/colors.gif"));
    QTRY_VERIFY(obj->status() == QSGImage::Ready);
    QTRY_VERIFY(obj->progress() == 1.0);
    QTRY_COMPARE(sourceSpy.count(), 1);
    QTRY_COMPARE(progressSpy.count(), 0);
    QTRY_COMPARE(statusSpy.count(), 0);

    // Loading remote file
    ctxt->setContextProperty("srcImage", "http://127.0.0.1:14449/stickman.gif");
    QTRY_VERIFY(obj->status() == QSGImage::Loading);
    QTRY_VERIFY(obj->progress() == 0.0);
    QTRY_VERIFY(obj->status() == QSGImage::Ready);
    QTRY_VERIFY(obj->progress() == 1.0);
    QTRY_COMPARE(sourceSpy.count(), 2);
    QTRY_VERIFY(progressSpy.count() > 1);
    QTRY_COMPARE(statusSpy.count(), 2);

    ctxt->setContextProperty("srcImage", "");
    QTRY_VERIFY(obj->status() == QSGImage::Null);
    QTRY_VERIFY(obj->progress() == 0.0);
    QTRY_COMPARE(sourceSpy.count(), 3);
    QTRY_VERIFY(progressSpy.count() > 2);
    QTRY_COMPARE(statusSpy.count(), 3);
}

QTEST_MAIN(tst_qsganimatedimage)

#include "tst_qsganimatedimage.moc"
<commit_msg>Skip another pixmap comparison test.<commit_after>/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtest.h>
#include <QtDeclarative/qdeclarativeengine.h>
#include <QtDeclarative/qdeclarativecomponent.h>
#include <QtDeclarative/qsgview.h>
#include <private/qsgrectangle_p.h>
#include <private/qsgimage_p.h>
#include <private/qsganimatedimage_p.h>
#include <QSignalSpy>
#include <QtDeclarative/qdeclarativecontext.h>

#include "../shared/testhttpserver.h"
#include "../../../shared/util.h"

#ifdef Q_OS_SYMBIAN
// In Symbian OS test data is located in applications private dir
#define SRCDIR "."
#endif

class tst_qsganimatedimage : public QObject
{
    Q_OBJECT
public:
    tst_qsganimatedimage() {}

private slots:
    void play();
    void pause();
    void stopped();
    void setFrame();
    void frameCount();
    void mirror_running();
    void mirror_notRunning();
    void mirror_notRunning_data();
    void remote();
    void remote_data();
    void sourceSize();
    void sourceSizeReadOnly();
    void invalidSource();
    void qtbug_16520();
    void progressAndStatusChanges();

};

void tst_qsganimatedimage::play()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickman.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QVERIFY(anim->isPlaying());

    delete anim;
}

void tst_qsganimatedimage::pause()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanpause.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QVERIFY(anim->isPlaying());
    QVERIFY(anim->isPaused());

    delete anim;
}

void tst_qsganimatedimage::stopped()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanstopped.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QVERIFY(!anim->isPlaying());
    QCOMPARE(anim->currentFrame(), 0);

    delete anim;
}

void tst_qsganimatedimage::setFrame()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanpause.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QVERIFY(anim->isPlaying());
    QCOMPARE(anim->currentFrame(), 2);

    delete anim;
}

void tst_qsganimatedimage::frameCount()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/colors.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QVERIFY(anim->isPlaying());
    QCOMPARE(anim->frameCount(), 3);

    delete anim;
}

void tst_qsganimatedimage::mirror_running()
{
    // test where mirror is set to true after animation has started

    QSGView *canvas = new QSGView;
    canvas->show();

    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/hearts.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(canvas->rootObject());
    QVERIFY(anim);

    int width = anim->property("width").toInt();

    QCOMPARE(anim->currentFrame(), 0);
    QPixmap frame0 = canvas->renderPixmap();
    anim->setCurrentFrame(1);
    QPixmap frame1 = canvas->renderPixmap();

    anim->setCurrentFrame(0);

    QSignalSpy spy(anim, SIGNAL(frameChanged()));
    anim->setPlaying(true);

    QTRY_VERIFY(spy.count() == 1); spy.clear();
    anim->setProperty("mirror", true);

    QCOMPARE(anim->currentFrame(), 1);
    QPixmap frame1_flipped = canvas->renderPixmap();

    QTRY_VERIFY(spy.count() == 1); spy.clear();
    QCOMPARE(anim->currentFrame(), 0);  // animation only has 2 frames, should cycle back to first
    QPixmap frame0_flipped = canvas->renderPixmap();

    QSKIP("Skip while QTBUG-19351 and QTBUG-19252 are not resolved", SkipSingle);

    QTransform transform;
    transform.translate(width, 0).scale(-1, 1.0);
    QPixmap frame0_expected = frame0.transformed(transform);
    QPixmap frame1_expected = frame1.transformed(transform);

    QCOMPARE(frame0_flipped, frame0_expected);
    QCOMPARE(frame1_flipped, frame1_expected);

    delete canvas;
}

void tst_qsganimatedimage::mirror_notRunning()
{
    QFETCH(QUrl, fileUrl);

    QSGView *canvas = new QSGView;
    canvas->show();

    canvas->setSource(fileUrl);
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(canvas->rootObject());
    QVERIFY(anim);

    int width = anim->property("width").toInt();
    QPixmap screenshot = canvas->renderPixmap();

    QTransform transform;
    transform.translate(width, 0).scale(-1, 1.0);
    QPixmap expected = screenshot.transformed(transform);

    int frame = anim->currentFrame();
    bool playing = anim->isPlaying();
    bool paused = anim->isPlaying();

    anim->setProperty("mirror", true);
    screenshot = canvas->renderPixmap();

    QSKIP("Skip while QTBUG-19351 and QTBUG-19252 are not resolved", SkipSingle);
    QCOMPARE(screenshot, expected);

    // mirroring should not change the current frame or playing status
    QCOMPARE(anim->currentFrame(), frame);
    QCOMPARE(anim->isPlaying(), playing);
    QCOMPARE(anim->isPaused(), paused);

    delete canvas;
}

void tst_qsganimatedimage::mirror_notRunning_data()
{
    QTest::addColumn<QUrl>("fileUrl");

    QTest::newRow("paused") << QUrl::fromLocalFile(SRCDIR "/data/stickmanpause.qml");
    QTest::newRow("stopped") << QUrl::fromLocalFile(SRCDIR "/data/stickmanstopped.qml");
}

void tst_qsganimatedimage::remote()
{
    QFETCH(QString, fileName);
    QFETCH(bool, paused);

    TestHTTPServer server(14449);
    QVERIFY(server.isValid());
    server.serveDirectory(SRCDIR "/data");

    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl("http://127.0.0.1:14449/" + fileName));
    QTRY_VERIFY(component.isReady());

    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);

    QTRY_VERIFY(anim->isPlaying());
    if (paused) {
        QTRY_VERIFY(anim->isPaused());
        QCOMPARE(anim->currentFrame(), 2);
    }
    QVERIFY(anim->status() != QSGAnimatedImage::Error);

    delete anim;
}

void tst_qsganimatedimage::sourceSize()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanscaled.qml"));
    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);
    QCOMPARE(anim->width(),240.0);
    QCOMPARE(anim->height(),180.0);
    QCOMPARE(anim->sourceSize(),QSize(160,120));

    delete anim;
}

void tst_qsganimatedimage::sourceSizeReadOnly()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/stickmanerror1.qml"));
    QVERIFY(component.isError());
    QCOMPARE(component.errors().at(0).description(), QString("Invalid property assignment: \"sourceSize\" is a read-only property"));
}

void tst_qsganimatedimage::remote_data()
{
    QTest::addColumn<QString>("fileName");
    QTest::addColumn<bool>("paused");

    QTest::newRow("playing") << "stickman.qml" << false;
    QTest::newRow("paused") << "stickmanpause.qml" << true;
}

void tst_qsganimatedimage::invalidSource()
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine);
    component.setData("import QtQuick 2.0\n AnimatedImage { source: \"no-such-file.gif\" }", QUrl::fromLocalFile(""));
    QVERIFY(component.isReady());

    QTest::ignoreMessage(QtWarningMsg, "file::2:2: QML AnimatedImage: Error Reading Animated Image File file:no-such-file.gif");

    QSGAnimatedImage *anim = qobject_cast<QSGAnimatedImage *>(component.create());
    QVERIFY(anim);

    QVERIFY(!anim->isPlaying());
    QVERIFY(!anim->isPaused());
    QCOMPARE(anim->currentFrame(), 0);
    QCOMPARE(anim->frameCount(), 0);
    QTRY_VERIFY(anim->status() == 3);
}

void tst_qsganimatedimage::qtbug_16520()
{
    TestHTTPServer server(14449);
    QVERIFY(server.isValid());
    server.serveDirectory(SRCDIR "/data");

    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/qtbug-16520.qml"));
    QTRY_VERIFY(component.isReady());

    QSGRectangle *root = qobject_cast<QSGRectangle *>(component.create());
    QVERIFY(root);
    QSGAnimatedImage *anim = root->findChild<QSGAnimatedImage*>("anim");

    anim->setProperty("source", "http://127.0.0.1:14449/stickman.gif");

    QTRY_VERIFY(anim->opacity() == 0);
    QTRY_VERIFY(anim->opacity() == 1);

    delete anim;
}

void tst_qsganimatedimage::progressAndStatusChanges()
{
    TestHTTPServer server(14449);
    QVERIFY(server.isValid());
    server.serveDirectory(SRCDIR "/data");

    QDeclarativeEngine engine;
    QString componentStr = "import QtQuick 2.0\nAnimatedImage { source: srcImage }";
    QDeclarativeContext *ctxt = engine.rootContext();
    ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/stickman.gif"));
    QDeclarativeComponent component(&engine);
    component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
    QSGImage *obj = qobject_cast<QSGImage*>(component.create());
    QVERIFY(obj != 0);
    QVERIFY(obj->status() == QSGImage::Ready);
    QTRY_VERIFY(obj->progress() == 1.0);

    QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(const QUrl &)));
    QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal)));
    QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QSGImageBase::Status)));

    // Loading local file
    ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/colors.gif"));
    QTRY_VERIFY(obj->status() == QSGImage::Ready);
    QTRY_VERIFY(obj->progress() == 1.0);
    QTRY_COMPARE(sourceSpy.count(), 1);
    QTRY_COMPARE(progressSpy.count(), 0);
    QTRY_COMPARE(statusSpy.count(), 0);

    // Loading remote file
    ctxt->setContextProperty("srcImage", "http://127.0.0.1:14449/stickman.gif");
    QTRY_VERIFY(obj->status() == QSGImage::Loading);
    QTRY_VERIFY(obj->progress() == 0.0);
    QTRY_VERIFY(obj->status() == QSGImage::Ready);
    QTRY_VERIFY(obj->progress() == 1.0);
    QTRY_COMPARE(sourceSpy.count(), 2);
    QTRY_VERIFY(progressSpy.count() > 1);
    QTRY_COMPARE(statusSpy.count(), 2);

    ctxt->setContextProperty("srcImage", "");
    QTRY_VERIFY(obj->status() == QSGImage::Null);
    QTRY_VERIFY(obj->progress() == 0.0);
    QTRY_COMPARE(sourceSpy.count(), 3);
    QTRY_VERIFY(progressSpy.count() > 2);
    QTRY_COMPARE(statusSpy.count(), 3);
}

QTEST_MAIN(tst_qsganimatedimage)

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