Adding C# interop project and NuGet package.
2 // Copyright (C) 2014-2015 Stéphane Lenclud.
4 // This file is part of MiniDisplay.
6 // MiniDisplay is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // MiniDisplay is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with MiniDisplay. If not, see <http://www.gnu.org/licenses/>.
20 #include "FutabaGP1212A01.h"
22 const int KNumberOfFrameBeforeDiffAlgo = 3;
28 GP1212A01A::GP1212A01A():
29 iDisplayPositionX(0),iDisplayPositionY(0),
31 iUseFrameDifferencing(true),
38 iNeedFullFrameUpdate(0)
45 GP1212A01A::~GP1212A01A()
60 iNeedFullFrameUpdate=0;
65 int GP1212A01A::Open()
67 int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A01A,NULL);
70 //Allocate both frames
73 iFrameAlpha=new BitArrayHigh(KGP12xFrameBufferPixelCount);
77 iFrameBeta=new BitArrayHigh(KGP12xFrameBufferPixelCount);
81 iFrameGamma=new BitArrayHigh(KGP12xFrameBufferPixelCount);
83 iFrameNext=iFrameAlpha;
84 iFrameCurrent=iFrameBeta;
85 iFramePrevious=iFrameGamma;
88 //To make sure it is synced properly
89 iNeedFullFrameUpdate=0;
92 //Since we can't get our display position we force it to our default
93 //This makes sure frames are in sync from the start
94 //Clever clients will have taken care of putting back frame (0,0) before closing
95 SetDisplayPosition(iDisplayPositionX,iDisplayPositionY);
102 void GP1212A01A::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel)
105 //int byteOffset=(aX*HeightInPixels()+aY)/8;
106 //int bitOffset=(aX*HeightInPixels()+aY)%8;
107 //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
109 //Pixel is on if any of the non-alpha component is not null
110 bool on = (aPixel&0x00FFFFFF)!=0x00000000;
116 iFrameNext->SetBit(aX*HeightInPixels()+aY);
120 iFrameNext->ClearBit(aX*HeightInPixels()+aY);
125 //Just specify a one pixel block
126 SetPixelBlock(aX,aY,0x00,0x01,on);
133 void GP1212A01A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
135 //TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions.
136 for (int i=0;i<aSrcWidth;i++)
138 for (int j=0;j<aSrcHeight;j++)
140 iFrameNext->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
147 Clear our client side back buffer.
148 Call to SwapBuffers must follow to actually clear the display.
150 void GP1212A01A::Clear()
152 //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes());
155 iFrameNext->ClearAll();
165 Must be followed by a SwapBuffers call.
167 void GP1212A01A::Fill()
173 Set all pixels on our screen to the desired value.
174 This operation is performed off screen to avoid tearing.
175 @param 8 pixels pattern
177 void GP1212A01A::SetAllPixels(unsigned char aPattern)
179 //With a single buffer
180 //unsigned char screen[2048]; //One screen worth of pixels
181 //memset(screen,0xFF,sizeof(screen));
182 //SetPixelBlock(0,0,63,sizeof(screen),screen);
187 memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
191 //Using pattern SetPixelBlock variant.
192 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),aPattern);
199 Set the defined pixel block to the given value.
200 @param X coordinate of our pixel block starting point.
201 @param Y coordinate of our pixel block starting point.
202 @param The height of our pixel block.
203 @param The size of our pixel data. Number of pixels divided by 8.
204 @param The value set to 8 pixels used as a pattern.
206 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue)
208 OffScreenTranslation(aX,aY);
209 FutabaVfdReport report;
210 report[0]=0x00; //Report ID
211 report[1]=(aSize<=report.Size()-10?aSize+0x08:64); //Report length. -10 is for our header first 10 bytes. +8 is for our Futaba header size
212 report[2]=0x1B; //Command ID
213 report[3]=0x5B; //Command ID
214 report[4]=0xF0; //Command ID
217 report[7]=aHeight; //Y length before return. Though outside the specs, setting this to zero apparently allows us to modify a single pixel without touching any other.
218 report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
219 report[9]=aSize; //Size of pixel data in bytes (LSB)
220 int sizeWritten=MIN(aSize,report.Size()-10);
221 memset(report.Buffer()+10, aValue, sizeWritten);
224 int remainingSize=aSize;
225 //We need to keep on sending our pixel data until we are done
226 while (report[1]==64)
229 remainingSize-=sizeWritten;
230 report[0]=0x00; //Report ID
231 report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
232 sizeWritten=(report[1]==64?63:report[1]);
233 memset(report.Buffer()+2, aValue, sizeWritten);
239 Set the defined pixel block to the given value.
240 @param X coordinate of our pixel block starting point.
241 @param Y coordinate of our pixel block starting point.
242 @param The height of our pixel block.
243 @param The size of our pixel data. Number of pixels divided by 8.
244 @param Pointer to our pixel data.
246 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
248 OffScreenTranslation(aX,aY);
249 FutabaVfdReport report;
250 report[0]=0x00; //Report ID
251 report[1]=(aSize<=report.Size()-10?aSize+0x08:64); //Report length. -10 is for our header first 10 bytes. +8 is for our Futaba header size
252 report[2]=0x1B; //Command ID
253 report[3]=0x5B; //Command ID
254 report[4]=0xF0; //Command ID
257 report[7]=aHeight; //Y length before return. Though outside the specs, setting this to zero apparently allows us to modify a single pixel without touching any other.
258 report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
259 report[9]=aSize; //Size of pixel data in bytes (LSB)
260 int sizeWritten=MIN(aSize,report.Size()-10);
261 memcpy(report.Buffer()+10, aPixels, sizeWritten);
264 int remainingSize=aSize;
265 //We need to keep on sending our pixel data until we are done
266 while (report[1]==64)
269 remainingSize-=sizeWritten;
270 report[0]=0x00; //Report ID
271 report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
272 sizeWritten=(report[1]==64?63:report[1]);
273 memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
279 Using this function is advised against as is causes tearing.
282 void GP1212A01A::SendClearCommand()
285 //Send Clear Display Command
286 FutabaVfdReport report;
287 report[0]=0x00; //Report ID
288 report[1]=0x04; //Report length
289 report[2]=0x1B; //Command ID
290 report[3]=0x5B; //Command ID
291 report[4]=0x32; //Command ID
292 report[5]=0x4A; //Command ID
297 Change our display position within our buffer.
299 void GP1212A01A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY)
302 //Send Display Position Settings Command
303 FutabaVfdReport report;
304 report[0]=0x00; //Report ID
305 report[1]=0x05; //Report length
306 report[2]=0x1B; //Command ID
307 report[3]=0x5B; //Command ID
308 report[4]=aDw; //Specify our DW
309 report[5]=aX; //X coordinate of our DW top-left corner
310 report[6]=aY; //Y coordinate of our DW top-left corner
315 Change our display position within our buffer.
317 void GP1212A01A::SetDisplayPosition(unsigned char aX, unsigned char aY)
319 //Specs apparently says both DW should remain the same
321 SetDisplayPosition(GP1212A01A::DW1,aX,aY);
322 SetDisplayPosition(GP1212A01A::DW2,aX,aY);
323 iDisplayPositionX=aX;
324 iDisplayPositionY=aY;
328 Provide Y coordinate of our off screen buffer.
330 unsigned char GP1212A01A::OffScreenY() const
332 //Overflowing is fine this is just what we want
333 return iDisplayPositionY+HeightInPixels();
337 Put our off screen buffer on screen.
338 On screen buffer goes off screen.
340 void GP1212A01A::SwapBuffers()
342 //Only perform buffer swapping if off screen mode is enabled
345 //Send host back buffer to device back buffer
346 if (!iUseFrameDifferencing || iNeedFullFrameUpdate<KNumberOfFrameBeforeDiffAlgo)
348 iNeedFullFrameUpdate++;
349 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),iFrameNext->Ptr());
353 //Frame diff algo is enabled
354 //We are going to send to our device only the differences between next frame and previous frame
355 SendModifiedPixelBlocks();
357 //Swap device front and back buffer
358 SetDisplayPosition(iDisplayPositionX,OffScreenY());
360 //Cycle through our frame buffers
361 //We keep track of previous frame which is in fact our device back buffer.
362 //We can then compare previous and next frame and send only the differences to our device.
363 //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
364 //Keep our previous frame pointer
365 BitArrayHigh* previousFrame=iFramePrevious;
366 //Current frame becomes the previous one
367 iFramePrevious = iFrameCurrent;
368 //Next frame becomes the current one
369 iFrameCurrent = iFrameNext;
370 //Next frame is now our former previous
371 iFrameNext = previousFrame;
376 //Define the edge of our pixel block
377 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
378 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
379 const int KPixelBlockEdge = 32;
380 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
381 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
385 * @brief GP1212A01A::SendModifiedPixelBlocks
386 * Compare our back and front buffer and send to the device only the modified pixels.
388 void GP1212A01A::SendModifiedPixelBlocks()
390 int w=WidthInPixels();
391 int h=HeightInPixels();
394 //TODO: optimize with memcmp and 16 inc
397 for (int i=0;i<w;i++)
399 for (int j=0;j<h;j++)
401 //aX*HeightInPixels()+aY
402 if ((*iFrameNext)[i*h+j]!=(*iFramePrevious)[i*h+j])
404 //We need to update that pixel
405 SetPixelBlock(i,j,0,1,((*iFrameNext)[i*h+j]?0x01:0x00));
406 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
407 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
409 //SetPixelBlock(i,j,15,32,iNextFrame->Ptr()+offset);
415 BitArrayHigh nextBlock(KPixelBlockSizeInBits);
416 BitArrayHigh previousBlock(KPixelBlockSizeInBits);
418 for (int i=0;i<w;i+=KPixelBlockEdge)
420 for (int j=0;j<h;j+=KPixelBlockEdge)
422 //aX*HeightInPixels()+aY
423 //int offset=(i*w/8)+(j/8);
425 #ifdef DEBUG_FRAME_DIFF
426 QImage imagePrevious(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
427 QImage imageNext(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
430 //Get both our blocks from our buffers
431 for (int x=i;x<i+KPixelBlockEdge;x++)
433 for (int y=j;y<j+KPixelBlockEdge;y++)
435 int blockOffset=(x-i)*KPixelBlockEdge+(y-j);
436 int frameOffset=x*h+y;
437 nextBlock.SetBitValue(blockOffset,(*iFrameNext)[frameOffset]);
438 previousBlock.SetBitValue(blockOffset,(*iFramePrevious)[frameOffset]);
440 #ifdef DEBUG_FRAME_DIFF
441 imageNext.setPixel(x-i,y-j,(nextBlock[blockOffset]?0xFFFFFFFF:0x00000000));
442 imagePrevious.setPixel(x-i,y-j,(previousBlock[blockOffset]?0xFFFFFFFF:0x00000000));
447 #ifdef DEBUG_FRAME_DIFF
448 QString previousName;
450 QTextStream(&previousName) << "p" << i << "x" << j << ".png";
451 QTextStream(&nextName) << "n" << i << "x" << j << ".png";
452 imagePrevious.save(previousName);
453 imageNext.save(nextName);
457 //if (memcmp(iFrameNext->Ptr()+offset,iFramePrevious->Ptr()+offset,32 )) //32=(16*16/8)
458 if (memcmp(nextBlock.Ptr(),previousBlock.Ptr(),KPixelBlockSizeInBytes)!=0)
460 //We need to update that block
461 SetPixelBlock(i,j,KPixelBlockEdge-1,KPixelBlockSizeInBytes,nextBlock.Ptr());
462 //SetPixelBlock(i,j,15,32,0xFF/*nextBlock.Ptr()*/);
463 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
464 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
466 //SetPixelBlock(i,j,15,32,iFrameNext->Ptr()+offset);
474 Translate the given pixel coordinate according to our off screen mode.
476 void GP1212A01A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
480 aX+=WidthInPixels()-iDisplayPositionX;
481 aY+=HeightInPixels()-iDisplayPositionY;
488 void GP1212A01A::ResetBuffers()
490 //iNextFrame->ClearAll();
491 //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
492 //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
498 void GP1212A01A::Request(TMiniDisplayRequest aRequest)
502 case EMiniDisplayRequestDeviceId:
505 case EMiniDisplayRequestFirmwareRevision:
506 RequestFirmwareRevision();
508 case EMiniDisplayRequestPowerSupplyStatus:
509 RequestPowerSupplyStatus();
519 void GP1212A01A::RequestDeviceId()
521 if (RequestPending())
523 //Abort silently for now
527 //1BH,5BH,63H,49H,44H
528 //Send Read ID command
529 FutabaVfdReport report;
530 report[0]=0x00; //Report ID
531 report[1]=0x05; //Report length
532 report[2]=0x1B; //Command ID
533 report[3]=0x5B; //Command ID
534 report[4]=0x63; //Command ID
535 report[5]=0x49; //Command ID
536 report[6]=0x44; //Command ID
537 if (Write(report)==report.Size())
539 SetRequest(EMiniDisplayRequestDeviceId);
545 void GP1212A01A::RequestFirmwareRevision()
547 if (RequestPending())
549 //Abort silently for now
553 //1BH,5BH,63H,46H,52H
554 //Send Software Revision Read Command
555 FutabaVfdReport report;
556 report[0]=0x00; //Report ID
557 report[1]=0x05; //Report length
558 report[2]=0x1B; //Command ID
559 report[3]=0x5B; //Command ID
560 report[4]=0x63; //Command ID
561 report[5]=0x46; //Command ID
562 report[6]=0x52; //Command ID
563 if (Write(report)==report.Size())
565 SetRequest(EMiniDisplayRequestFirmwareRevision);
571 void GP1212A01A::RequestPowerSupplyStatus()
573 if (RequestPending())
575 //Abort silently for now
578 //1BH,5BH,63H,50H,4DH
579 //Send Power Suppply Monitor Command
580 FutabaVfdReport report;
581 report[0]=0x00; //Report ID
582 report[1]=0x05; //Report length
583 report[2]=0x1B; //Command ID
584 report[3]=0x5B; //Command ID
585 report[4]=0x63; //Command ID
586 report[5]=0x50; //Command ID
587 report[6]=0x4D; //Command ID
588 if (Write(report)==report.Size())
590 SetRequest(EMiniDisplayRequestPowerSupplyStatus);
596 This is for development purposes only.
597 Production application should stick to off-screen mode to avoid tearing.
599 void GP1212A01A::ToggleOffScreenMode()
601 SetOffScreenMode(!iOffScreenMode);
605 * @brief GP1212A01A::SetOffScreenMode
609 void GP1212A01A::SetOffScreenMode(bool aOn)
611 if (aOn==iOffScreenMode)
619 //Clean up our buffers upon switching modes
620 SetDisplayPosition(0,0);
627 Tries to complete our current request if we have one pending.
629 TMiniDisplayRequest GP1212A01A::AttemptRequestCompletion()
631 if (!RequestPending())
633 return EMiniDisplayRequestNone;
636 int res=Read(iInputReport);
640 return EMiniDisplayRequestNone;
643 //Process our request
644 if (CurrentRequest()==EMiniDisplayRequestPowerSupplyStatus)
646 if (iInputReport[1]==0x4F && iInputReport[2]==0x4E)
650 else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46)
655 else if (CurrentRequest()==EMiniDisplayRequestDeviceId)
657 unsigned char* ptr=&iInputReport[1];
658 strcpy(iDeviceId,(const char*)ptr);
660 else if (CurrentRequest()==EMiniDisplayRequestFirmwareRevision)
662 unsigned char* ptr=&iInputReport[1];
663 strcpy(iFirmwareRevision,(const char*)ptr);
666 TMiniDisplayRequest completed=CurrentRequest();
667 //Our request was completed
668 SetRequest(EMiniDisplayRequestNone);
675 Set our screen brightness.
676 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
678 void GP1212A01A::SetBrightness(int aBrightness)
680 if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
682 //Brightness out of range.
683 //Just ignore that request.
687 FutabaVfdReport report;
688 report[0]=0x00; //Report ID
689 report[1]=0x06; //Report size
690 report[2]=0x1B; //Command ID
691 report[3]=0x5C; //Command ID
692 report[4]=0x3F; //Command ID
693 report[5]=0x4C; //Command ID
694 report[6]=0x44; //Command ID
695 report[7]=0x30+aBrightness; //Brightness level